252 Commits

Author SHA1 Message Date
18a6858edd UI fix for UHD monitors 2020-09-14 20:33:09 -04:00
9315111793 Add tsc notes 2020-09-14 20:32:49 -04:00
60c763167d More work on debugger 2020-08-09 21:18:30 -04:00
3ae119072c Fix dissassembly display error 2020-08-09 20:26:42 -04:00
2ace932b52 Fix tests 2020-08-01 21:22:13 -04:00
50b95d1a9c Trap flag / Debugger first draft #232 2020-08-01 20:27:39 -04:00
ad08a5ac4f Fix #229 2020-07-31 19:27:14 -04:00
84a9132f5e Division by zero triggers INT #231 2020-07-31 18:34:57 -04:00
2ff19f24eb INTO instruction #78 2020-07-31 18:11:00 -04:00
b1d7121b22 Interrupts WIP #230 2020-07-30 21:26:47 -04:00
51c0d4c3c7 SETA test to confirm I can close #184 2020-07-30 20:00:15 -04:00
421a983910 More tests, add octal with '0o' prefix, fixes #166 2020-07-30 19:27:41 -04:00
73dc9b1dca More operand/dw tests (#228) 2020-07-30 19:13:07 -04:00
fe299fe061 Test for DW directive with strings (#228) 2020-07-30 18:40:34 -04:00
319abad130 Test for ORG directive (#228) 2020-07-30 18:38:19 -04:00
ad0124508c More refactoring, easier assembly testing (#227) 2020-07-29 21:08:52 -04:00
ba78d2fd93 Merge mischw plugin with cubot package 2020-07-29 19:13:11 -04:00
cc5627902e Remove NpcPlugin class 2020-07-29 18:58:34 -04:00
21a391e077 Simplify project structure (wip), editor keymap, UI tweaks 2020-07-28 20:09:51 -04:00
3b7cff6ac7 Hide phaser banner 2020-07-28 20:03:47 -04:00
304c063887 Simplify project structure (wip), run tsc during compilation 2020-07-27 19:38:29 -04:00
cd9e555e86 Merge pull request #226 from kevinramharak/fix-client-size-console-buffer-update
fix faulty object property name
2020-07-27 13:06:32 -04:00
Kevin Ramharak
7c0187f514 fix faulty object property name 2020-07-27 17:25:05 +02:00
ac27250f98 Merge pull request #225 from simon987/dependabot/maven/Server/org.springframework.security-spring-security-core-5.1.11.RELEASE
Bump spring-security-core from 5.1.5.RELEASE to 5.1.11.RELEASE in /Server
2020-06-15 16:10:53 -04:00
dependabot[bot]
84f59a5fa2 Bump spring-security-core in /Server
Bumps [spring-security-core](https://github.com/spring-projects/spring-security) from 5.1.5.RELEASE to 5.1.11.RELEASE.
- [Release notes](https://github.com/spring-projects/spring-security/releases)
- [Commits](https://github.com/spring-projects/spring-security/compare/5.1.5.RELEASE...5.1.11.RELEASE)

Signed-off-by: dependabot[bot] <support@github.com>
2020-06-15 19:36:46 +00:00
a0193621a8 Remove jenkins folder 2020-05-29 18:34:14 -04:00
922162660f Update footer.vm 2020-02-19 22:38:36 -05:00
014dab49da Merge pull request #221 from OrdoFlammae/radioactivecloud
Added Radioactive Cloud object.
2019-11-28 13:11:57 -05:00
Ordo Flammae
4c81937edc Added Radioactive Cloud object. 2019-11-27 19:35:49 -06:00
2597b558e6 Update slack link in play page 2019-11-08 10:10:17 -05:00
ea1ec1b1b3 jenkins hotfix 2019-10-27 22:20:28 -04:00
8b55466e7c Update README.md 2019-10-27 16:39:50 -04:00
Adam
03ff2e5527 Refactoring & code style (#212) 2019-10-23 08:38:01 -04:00
Mauricio Rivera
634af8859f Code style 197 (#213) 2019-10-23 08:34:24 -04:00
yesji
39ca4d5143 Fixed "'module' is defined but never used." issue (#208)
* Fixed "'module' is defined but never used." issue
2019-10-13 09:13:27 -04:00
CaderHancock
5f3c34911c Overload methods are not grouped together (#210)
* Overload methods are not grouped together
2019-10-10 16:46:49 -04:00
yesji
638c5b3bb3 Fixed 3 "switch without 'default' clause." issues (#207)
Fixed 3 "switch without 'default' clause."
2019-10-06 08:51:32 -04:00
Simon Fortier
50e0baa6ec Merge pull request #206 from RichardJamesC/master
moved the overloaded parseDWInstruction() #197
2019-06-18 20:40:36 -04:00
Richard Cuthbert
3a35f3fdfc In Assembler.java, I moved the overloaded parseDWInstruction() method to be sequential to the earlier method of the same name. 2019-06-19 00:05:44 +01:00
Simon Fortier
2982fcbbc1 Merge pull request #204 from Xychen1994/code-refactor
simplified getRandomTileWithAdjacent with for loop instead of similar branches
2019-06-01 13:48:25 -04:00
Xinyuan Chen
fb97415ee7 simplified getRandomTileWithAdjacent 2019-05-30 22:57:19 -07:00
Simon Fortier
f37ae36262 Update dates 2019-05-22 18:34:59 -04:00
Simon Fortier
556b46badc Typo 2019-05-22 18:34:25 -04:00
Simon Fortier
d57f8d2970 Merge pull request #203 from MyGodItsFull0fStars/reduce_complexity_of_method
#197 simplified getAdjacentTileCount
2019-05-12 15:53:15 -04:00
Christian Bauer
629dac6ea3 getAdjacentTileCount Method now uses for-loop instead of many if statements 2019-05-12 21:40:52 +02:00
simon987
55fdd93d9d Update dependency 2019-04-23 17:14:24 -04:00
simon987
2f7d2cd85a Fix build fail and refactor config file in tests 2019-04-23 17:04:20 -04:00
Simon Fortier
6e4b2358af Merge pull request #201 from kevinramharak/fix-configuration-path-java-11-#200
fix tests and configuration
2019-04-23 16:39:35 -04:00
Kevin Ramharak
f9c7f02870 fix tests and configuration 2019-04-23 21:56:31 +02:00
Simon Fortier
86b331fc60 Merge pull request #199 from matias9477/master
Code style & refactor for two issues
2019-04-18 15:35:47 -04:00
Matias
35b6f06687 Code style & refactor for two issues 2019-04-18 14:54:37 -03:00
simon987
bcd96be625 Change mongo transaction warning message 2019-04-11 18:43:47 -04:00
Woosle Park
f99f327480 Issue 182: Leaderboard changes (#194)
Implement new stats
2019-04-11 18:35:41 -04:00
simon987
d71b3dc97c Add badges 2019-04-03 18:58:20 -04:00
simon987
e378016c56 Jenkins setup 2019-04-03 18:19:51 -04:00
Simon Fortier
c50cc8c364 Merge pull request #192 from kevinramharak/implement-SETcc-instructions#184
actually add the implemented instrutions to instruction set :)
2019-01-24 19:06:23 -05:00
Kevin Ramharak
bf9cdc7b29 actually add the implemented instrutions to instruction set :) 2019-01-24 19:17:01 +01:00
Simon Fortier
75410cc0db Merge pull request #191 from kevinramharak/implement-SETcc-instructions#184
Implement setcc instructions#184
2019-01-23 19:23:22 -05:00
simon987
e0ed1e0e6f reformat code 2019-01-23 19:19:57 -05:00
Kevin Ramharak
0fbd1d1a6b add setcc instruction to editor lexer 2019-01-23 17:21:52 +01:00
Kevin Ramharak
8018fb3e30 update syntax highlighting 2019-01-23 17:18:29 +01:00
Kevin Ramharak
fb8fdac9c3 add tests for all SETcc instructions 2019-01-23 14:25:58 +01:00
Kevin Ramharak
a3253e8e3a fix setg 2019-01-23 14:08:40 +01:00
Kevin Ramharak
9ee754e5be add forgoten alias 2019-01-23 13:47:42 +01:00
Kevin Ramharak
e72e8b45c5 implement setcc tests 2019-01-23 10:08:30 +01:00
Kevin Ramharak
311889bc93 fix operandValid check; remove unused import; 2019-01-23 10:08:15 +01:00
Kevin Ramharak
9cb2c29f0f remove logs; 1 log might cause a null ref; 2019-01-23 09:48:43 +01:00
Kevin Ramharak
f3ae97c060 fix typo 2019-01-23 09:43:33 +01:00
Kevin Ramharak
e8033f3291 add operand constructors for easier testing 2019-01-23 09:39:44 +01:00
Kevin Ramharak
b19eb0568d remove magic numbers 2019-01-23 09:31:49 +01:00
Kevin Ramharak
9aa876df62 implement all aliases as instructions 2019-01-23 09:16:56 +01:00
Kevin Ramharak
b1da29d7fb fix feedback 2019-01-23 09:16:44 +01:00
Kevin Ramharak
12f20d178a example implementation of setcc and first cc setz 2019-01-22 22:13:25 +01:00
Kevin Ramharak
a030e9fc5d slightly rework aliases to work with inheritance 2019-01-22 22:12:34 +01:00
Kevin Ramharak
439547102f remove package private attributes 2019-01-22 20:54:59 +01:00
Kevin Ramharak
d5ddb8e439 allow operand validation methods to be overriden 2019-01-22 20:54:26 +01:00
Kevin Ramharak
4f4cecf60c allow operand checks to be overriden 2019-01-22 20:17:26 +01:00
Kevin Ramharak
a1136f9056 make MachineCode publicly visible 2019-01-22 20:16:40 +01:00
Kevin Ramharak
d2e7084354 update encode throw signature 2019-01-22 19:20:07 +01:00
Kevin Ramharak
1157bd5df5 remove random newline 2019-01-22 19:19:42 +01:00
Kevin Ramharak
0ff671713c allow inherited classes to get its own opcode and mnemonic 2019-01-22 18:48:49 +01:00
Kevin Ramharak
2590e8ec64 Merge branch 'master' into implement-SETcc-instructions#184 2019-01-22 18:40:32 +01:00
Simon Fortier
a768cf0f4b Merge pull request #189 from KevinRamharak/possible-implementation-aliases
add implementation for instruction aliases
2019-01-21 19:55:17 -05:00
Kevin Ramharak
7b3839f046 change types to map interface, remove unused import 2019-01-20 19:17:43 +01:00
Kevin Ramharak
4958c3d9a0 add implementation for instruction aliases 2019-01-20 13:32:48 +01:00
Kevin Ramharak
f8eec652ae basic skeleton 2019-01-20 13:10:06 +01:00
simon987
762bad758b Fixes #188 #187 2019-01-15 21:02:25 -05:00
Kevin Ramharak
3e2422775e Merge branch 'master' into implement-SETcc-instructions#184 2019-01-15 19:32:19 +01:00
simon987
993838651f Fixes #185 2019-01-14 18:24:34 -05:00
simon987
ce8c5375ee Merge remote-tracking branch 'origin/master' 2019-01-14 18:16:45 -05:00
Simon Fortier
91ebb51389 Merge pull request #186 from KevinRamharak/master
This will allow for easier search / grep's to figure out what opcodes are used
2019-01-14 18:16:42 -05:00
simon987
d113f635a1 Merge remote-tracking branch 'origin/master' 2019-01-14 18:14:44 -05:00
Kevin Ramharak
78a9f78a54 fix merge conflict 2019-01-14 18:46:23 +01:00
Kevin Ramharak
4f8eb2725e fix typo 2019-01-14 18:37:42 +01:00
Kevin Ramharak
ff4f6c23eb adjust source code so all instructions use the same patterns 2019-01-14 18:32:53 +01:00
Kevin Ramharak
c5a34c7a3f adjust source code so all instructions use the same patterns 2019-01-14 18:32:27 +01:00
Kevin Ramharak
1d3d441996 allow instructions to override the encode method 2019-01-14 17:48:49 +01:00
simon987
7ba46ec36e Updated dependencies 2019-01-09 18:52:13 -05:00
simon987
63cdfb22ac Merge branch 'master' of https://github.com/simon987/Much-Assembly-Required 2019-01-09 18:47:01 -05:00
simon987
f9898f57f4 Updated dependencies 2019-01-09 18:46:37 -05:00
Data Archivist
2548242da8 Updated dependencies 2019-01-09 18:36:33 -05:00
simon
46d5c9b51e HackedNPC code section offset same as Cubot (for real this time) 2019-01-01 10:16:54 -05:00
simon
7fd1c35f7a Merge remote-tracking branch 'origin/master' 2018-12-23 12:47:38 -05:00
simon
0dbec1d258 HackedNPC code section offset same as Cubot 2018-12-23 12:47:32 -05:00
Simon Fortier
45d3b84dd0 Merge pull request #181 from simon987/blueprints
Oops I pushed to the wrong branch
2018-12-23 11:56:24 -05:00
simon
af4ddbdd89 Increased HackedNPC specs & reverted accidental change 2018-12-23 11:54:47 -05:00
simon
84ca3acb52 revert debug values 2018-12-22 18:17:53 -05:00
Simon Fortier
0973548b71 Merge pull request #179 from simon987/factory-rewrite
NPCPlugin refactor, Added HackedNPC
2018-12-22 17:55:12 -05:00
simon
5f95c17aed More refactoring, updated mongodb driver + transactions on supported clusters 2018-12-22 17:48:29 -05:00
simon
b361f87154 NPC Plugin refactoring 2018-12-22 15:29:58 -05:00
simon
e4a06e79d4 Hacked NPC minimum viable 2018-12-22 11:26:34 -05:00
simon
955d61ce99 Password moved from vault door to settlement 2018-12-21 14:04:42 -05:00
simon
201d83f8d8 Bump version 2018-12-21 14:00:47 -05:00
simon
94b8ef5395 NPC Plugin rewrite.
Plugin-level data can be stored in DB
2018-12-21 14:00:18 -05:00
simon
70eeb1442d Added Fluid Tile 2018-12-21 10:47:46 -05:00
simon
bbaa338469 Fix 500 internal error when autologin option is not specified in the config 2018-12-21 10:45:08 -05:00
Simon
5de909cd9c Fixed some debug commands. Added blueprint item deserialization, Added inventory SCAN and SEEK actions 2018-12-06 11:13:06 -05:00
Simon
71e88afdc9 Fixed typos & cleaned comments. Added autologin debug option. Added construction site base 2018-12-02 20:04:45 -05:00
Simon
ae41bd9cb9 Added construction plugin module 2018-11-30 21:51:34 -05:00
Simon
de45eb1827 Fixes #160 2018-11-30 16:35:58 -05:00
Simon
6c7a2f0a73 Fixes #169 2018-11-30 15:11:24 -05:00
Simon
3776070689 Fixes #170, #155 2018-11-27 16:02:21 -05:00
Simon
54ed05b86c Move hologram-related fields to proper class. Breaks existing database saves! 2018-11-27 14:38:33 -05:00
Simon
b361583252 Added memory location option for LiDAR GET_PATH. Fixes #140 2018-11-27 13:52:00 -05:00
Simon
04c837d692 Removed TEMPORARY guest policy. Fixes #25 2018-11-27 13:35:42 -05:00
Simon
aaeb18068d Removed useless argument 2018-11-27 13:26:20 -05:00
Simon
e848fd8b8a Updated Phaser to 2.11, Added magnetic tile sprite 2018-11-27 12:50:18 -05:00
Simon
1435d31d36 Implemented guest BLOCK and ALLOW options #25 2018-11-27 10:25:21 -05:00
Simon
950f6b6b4b Minor cleanup 2018-11-27 09:30:03 -05:00
Simon
6a9cfb3acb Cubot drill module now functional 2018-11-15 12:24:37 -05:00
Simon
4be1bf2e8a TileMap now uses Tile instances instead of ints. No changes in the way it is stored in the database 2018-11-15 12:08:44 -05:00
Simon Fortier
e50bcdeab7 Merge pull request #177 from Wiewiogr/148-secret-key
Add secret key generator #148
2018-10-28 13:17:17 -04:00
Wiewiogr
c8214236ab update properties on secret key change 2018-10-28 18:13:56 +01:00
Wiewiogr
37b7bbff98 Merge pull request #2 from Wiewiogr/master
merge
2018-10-28 18:12:37 +01:00
Wiewiogr
be7304aa0b Merge pull request #1 from simon987/master
merge
2018-10-28 17:50:01 +01:00
Simon
0845438297 Fixed potential error in ServerConfiguration and added ability to programmatically modify server configuration 2018-10-28 09:45:53 -04:00
Simon Fortier
564a692c2e Merge pull request #176 from Wiewiogr/138-add-items-container
Added Container object #138
2018-10-28 09:43:17 -04:00
Wiewiogr
a40a0712f0 changed fields names 2018-10-27 18:26:24 +02:00
Wiewiogr
f89c39c756 changed implementation to list 2018-10-27 18:13:43 +02:00
Wiewiogr
a6f0ce1dfb moved test to correct folder 2018-10-27 18:11:56 +02:00
Wiewiogr
2f80205b2a added secret_key property to config.properties 2018-10-27 17:23:07 +02:00
Wiewiogr
71f96f27d1 add secretKey field to GameServer 2018-10-27 17:22:20 +02:00
Wiewiogr
98402dd45b Added SecretKeyGenerator class 2018-10-27 17:21:12 +02:00
Wiewiogr
548d756e90 Added tests for ItemsContainer 2018-10-27 15:51:21 +02:00
Wiewiogr
1a2332bc32 Create ItemsContainer.java 2018-10-27 15:49:39 +02:00
Simon Fortier
9a73b7b7d1 Merge pull request #175 from senatormailman/readme
Added MacOS instructions
fixes #152
2018-10-12 18:49:43 -04:00
Liam Hogan
d860591cc8 Minor formatting fixes following simon987's suggestion. 2018-10-12 17:45:38 -04:00
Liam Hogan
268acda773 Minor formatting fix. 2018-10-12 15:35:30 -04:00
Liam Hogan
6d66e19dc5 Added installation instructions for macOS. Pulled some of the formatting from Anarcroth's Linux instructions. 2018-10-12 15:30:34 -04:00
Simon Fortier
40e7899cf6 Merge pull request #174 from Anarcroth/add-arch-install-instructions
Added Arch Linux installation instructions in README.md
2018-10-03 21:47:27 -04:00
Martin Nestorov
abb92bfc75 Added Arch Linux installation instructions 2018-10-03 20:06:49 +03:00
Simon Fortier
becf6e5feb Merge pull request #173 from bobbyjudd/issue-172
Fixes issue 172
2018-09-06 15:56:07 -04:00
Bobby Judd
8af652482d Fixed memory displacement when subtracting labels, and added corresponding test 2018-09-05 21:11:11 -07:00
Simon
205845d6af Changed the way port configuration is done 2018-09-03 17:04:29 -04:00
Simon
64193ecf7a Should fix user creation problem 2018-09-03 16:12:36 -04:00
Simon Fortier
a1cf279b6f Merge pull request #171 from Francessco121/bug/unicode-string-crash
Fix assembler crash when invalid Unicode string escape sequences are used
2018-08-30 17:24:32 -04:00
Ethan Lafrenais
6074238131 Catch string unescape exceptions when parsing DW string operands 2018-08-29 23:15:06 -04:00
Ethan Lafrenais
fa62c49fa8 Add the Visual Studio Code workspace directory to .gitignore 2018-08-29 23:13:08 -04:00
Simon
a0aaee7ee7 Added find cubot button 2018-08-13 15:59:46 -04:00
Simon
96fc3ed68c Should fix server crash problem when harvesting biomass 2018-08-13 15:43:41 -04:00
Simon
e69111ff3c Should fix user creation problem 2018-08-13 15:41:24 -04:00
Simon Fortier
e7e9a3e6fb Merge pull request #168 from Mozzi20/master
Added radioactive obstacle
2018-06-08 12:09:36 -04:00
Unknown
a73aa7c3a5 Moved radioactive obstacle. 2018-06-08 09:58:52 +02:00
Unknown
e8543082ce Fixed radioactive obstacle.
Added "radioactive obstacle curruption block size" in config.properties and removed its getter and setter.
2018-06-07 23:24:49 +02:00
Unknown
4b7ebd7ad6 Added radioactive obstacle. 2018-06-07 21:40:59 +02:00
Simon
c389bbc92e Refactored SocketServer.tick() method 2018-06-04 18:06:31 -04:00
Simon
78f98c8227 JSONSerialisable objects now have a debug function #156 2018-06-04 17:07:20 -04:00
Simon
92008e553a ServerPlugin no longer implements JSONSerialisable (this feature was unused) 2018-06-04 17:06:11 -04:00
Simon
45d34c37ad Commented out some debug messages & fix for #161 2018-06-04 16:44:32 -04:00
Simon
a9cc9662f4 Changed unreliable sequential integer object id to BSON ObjectId #162 2018-06-04 14:53:20 -04:00
Simon
df9c466827 Avoid thread safety issues with objectIds #162 2018-06-04 12:18:21 -04:00
Simon
b65c57ceba Fixed exception showing when tick-based broadcast happens exactly when a user is connected but not authenticated pt.2 2018-06-04 11:46:31 -04:00
Simon
2f74dd45fc Reformat + Should fix #159 2018-06-04 10:49:34 -04:00
Simon
07160138aa Fixed exception showing when tick-based broadcast happens exactly when a user is connected but not authenticated 2018-06-04 10:46:55 -04:00
Simon
b6a206f3c7 Added basic Cubot.toString() and fixed account page debug info 2018-05-31 22:33:56 -04:00
Simon
ed1c4cff0b Fixed bug that prevents RadioReceiverHardware from being correctly loaded from database 2018-05-31 22:28:35 -04:00
Simon
a005e2b163 Reverted accidental change caused by search and replace 2018-05-31 22:21:33 -04:00
Simon
a7bdbd2513 I broke everything pt. 1: Moved helditem into CubotInventory, added debug commands for CubotInventory, Added vault complete GameEvent, moved CPU to Cubot, renamed CpuHardware to HardwareModule, moved HardwareModule to Cubot & added HardwareHost interface, removed getParent() in ControllableUnit, moved items to the Item class, started moving tiles to Tile classes, renamed Programmable to MessageReceiver 2018-05-31 22:13:07 -04:00
simon
ee9eeeef55 Reverted value of factory_spawn_rate that was accidentally changed 2018-05-12 20:33:41 -04:00
simon
4b67798180 Added structure class #145 and moved many constants to config.properties 2018-05-12 20:32:10 -04:00
simon
be8dd14d36 Added method to find objects of particular type in a world 2018-05-12 19:09:00 -04:00
simon
80f45f1eb0 Added catch clause to prevent crash when socket is not opened 2018-05-12 19:07:55 -04:00
simon
3368268924 Cubot object id is no longer stored inside every CpuHardware in the database. Renamed some fields in the database to make them more readable 2018-05-12 16:16:24 -04:00
simon
4cd58c86a5 Refactor: changed the way game objects and cpu hardware are saved/loaded from the database #151 2018-05-12 15:32:42 -04:00
simon
8d029cf621 Added vagrant instructions in readme #152 2018-05-11 21:23:41 -04:00
simon
10f088cb66 Updated gitattributes (again) 2018-05-11 21:19:36 -04:00
simon
159c217d59 Updated gitattributes 2018-05-11 21:18:25 -04:00
Simon Fortier
c5cb5df335 Merge pull request #153 from simon987/spark
Moved frontend to main server application
2018-05-11 21:13:01 -04:00
simon
854863ede9 Updated mongodb driver to 3.7.0 2018-05-11 21:06:18 -04:00
simon
e98575b23f Added vagrant support 2018-05-11 21:04:00 -04:00
simon
1a5d12a19f updated Docker support 2018-05-11 16:42:03 -04:00
simon
1678be25c5 Added UserStats and updated leaderboard page 2018-05-11 13:16:34 -04:00
simon
9c41c16079 Added debug command to save the game 2018-05-11 10:25:40 -04:00
simon
e97ecbe380 Added javadocs 2018-05-01 15:41:51 -04:00
simon
083af31b84 Fixed websocket problem by upgrading spark version. Also fixed z-index problem with console 2018-04-29 17:00:42 -04:00
simon
2c856aae80 Editor upload & reload buttons are now working. Floppy upload & download working. 2018-04-29 14:00:50 -04:00
simon
315e33055e Reorganised css files, some work on the home page design, console screen is now in a resizable panel 2018-04-29 11:17:54 -04:00
simon
e025b6d2da SSL now working and reorganised web server 2018-04-28 12:06:55 -04:00
simon
e3a650a4fc Reorganised debug info in account page and extracted user loading to UserManager 2018-04-27 21:59:07 -04:00
simon
113aa50d87 Websocket auth up and running 2018-04-27 21:28:46 -04:00
simon
dc034d1437 Users can change their password 2018-04-27 20:39:14 -04:00
simon
3492e133e1 login/register working and Websocket partially implemented 2018-04-27 19:42:53 -04:00
simon
bd5f8573e8 Added web server and partly integrated frontend 2018-04-27 16:58:50 -04:00
simon
3c5bfdb30b Fixed build errors 2018-04-24 08:09:42 -04:00
simon987
9349ae108b Removed some unnecessary unit tests 2018-04-02 10:58:24 -04:00
simon987
292adb5483 Updated maven version numbers. Removed .iml files 2018-04-02 10:52:36 -04:00
simon987
e479d89375 Added Radioactive cloud plugin 2018-04-02 10:41:37 -04:00
simon987
41d674d74a Removed unused constant 2018-04-02 10:06:44 -04:00
Simon Fortier
227f7ce5aa Merge pull request #144 from mlaga97/ram-corruption
Implement RAM corruption effect
2018-04-01 22:18:15 -04:00
Luc Lagarde
a091071d55 Implement RAM corruption effect 2018-03-31 17:50:51 -05:00
Simon Fortier
f1b8f3dc6d Merge pull request #143 from mlaga97/lidar-vault-fix
Make LiDAR work in Vaults (fixes #140)
2018-03-29 20:10:44 -04:00
Luc Lagarde
e62a51ee2e Modify LIDAR_GET_MAP to write to a given memory address 2018-03-28 23:37:05 -05:00
Luc Lagarde
217c997788 Add action to get world size from LiDAR 2018-03-28 23:34:23 -05:00
simon
ff61433c4b Fixed typo 2018-03-11 16:09:28 -04:00
simon
cbde2450fa Bug fixes: maxShield is now set on user creation and vault objectId is set on world generation 2018-03-11 16:06:15 -04:00
simon
1d780f7d9b Minor refactor 2018-03-11 13:34:10 -04:00
simon
cbb07891fc Cubot respawns on death. ElectricBox damages near objects 2018-03-10 15:22:11 -05:00
simon
2565d3338c Implemented shield hardware. Added heal, damage and charge shield debug commands. 2018-03-10 13:54:43 -05:00
simon
e4269b83c4 Fixed hardware deserialization issues 2018-03-10 11:51:31 -05:00
simon
9cac665101 Debug commands are easier to add. Added comPortMsg command 2018-03-10 11:24:29 -05:00
simon
dc19176dc8 Merge branch 'master' into vaults
# Conflicts:
#	Server/src/main/java/net/simon987/server/game/GameObject.java
#	Server/src/main/java/net/simon987/server/game/World.java
#	Server/src/main/java/net/simon987/server/webserver/TerrainRequestHandler.java
2018-03-10 09:53:55 -05:00
simon
8ed192f8d0 Cubot implements Attackable (untested) 2018-03-10 09:49:34 -05:00
Simon Fortier
d1a3cf9307 Merge pull request #110 from djsheehy/shield-hardware
Added Shield hardware
2018-03-05 22:18:44 -05:00
simon
f8c5dac969 Added logging of vault clears for display in the leaderboard 2018-03-05 21:47:28 -05:00
simon
0a75cb557d Added objective vault world & its exit portal 2018-03-04 15:51:30 -05:00
simon
f35e6c5a9a Objects can enter & leave vaults 2018-03-04 14:56:02 -05:00
simon
0ada6c29d4 More work on vaults 2018-03-01 10:42:24 -05:00
simon
156deb8f4e NPCs decrement World.updatable on death. Fixes problem making Worlds stay loaded forever in RAM 2018-02-27 16:51:18 -05:00
simon
039088ac00 Added electric boxes, debug command to teleport objects across Worlds. 2018-02-27 16:49:32 -05:00
simon
f530dafdee Basic world generation for entire Vault dimension 2018-02-26 17:17:40 -05:00
simon
817dbcc6c4 Basic world generation for single Vault world 2018-02-26 13:29:08 -05:00
simon
62f1403cb3 Started working on Vault worlds generation 2018-02-26 10:04:06 -05:00
simon
8c6e580ea9 Added support for multiple dimensions 2018-02-25 14:15:03 -05:00
simon
6a1519d97d Added many debug commands 2018-02-25 11:55:53 -05:00
simon
95a14ad1ab Added basic functionality for debug commands 2018-02-17 10:05:53 -05:00
simon
187a828c79 Fixed opcode clash for PUSHF instruction 2018-01-20 09:40:23 -05:00
simon
ffca185fe5 Vault Door generation 2018-01-17 22:01:59 -05:00
simon
815f3de234 Changed the walking function to enable interaction with Enterable objects 2018-01-17 21:11:16 -05:00
simon
3505a466bb Fixed compilation errors 2018-01-17 20:31:35 -05:00
simon
3d10e4306b Merge branch 'master' into vaults
# Conflicts:
#	Server/src/main/java/net/simon987/server/GameServer.java
#	Server/src/main/java/net/simon987/server/game/World.java
2018-01-17 20:29:37 -05:00
simon
a285b3104e Moved Vault Door code to NPC Plugin. Fixed code styling. Fixed compilation errors 2018-01-14 13:33:40 -05:00
simon
947deea784 Merge remote-tracking branch 'origin/vaults' into vaults 2018-01-14 12:04:04 -05:00
simon
4293fc0315 Support for variable World size 2018-01-14 12:02:32 -05:00
Simon Fortier
f1c1f8f807 Merge pull request #120 from sg495/vaults
New additional code for Vault doors
2018-01-09 19:22:42 -05:00
sg495
a04207b5e0 Added some cyphers for use in vault doors.
1. Created net.simon987.server.crypto package and moved RandomStringGenerator there.
2. Created CryptoProvider class, added a global instance to GameServer for use by game entities.
3. Created interface for cyphers, abstract class for shift substitution cyphers, classes for no cypher, Caesar cypher, Vigenere cypher and autokey cypher.
4. Created some Crypto exceptions.
5. Removed static encryption/decryption methods from VaultDoor, and moved getRandomPassword() to the global CryptoProvider instance.
2018-01-09 17:14:31 +01:00
sg495
46483b2bf8 Added a RandomString class from stackoverflow, to generate random alphanumeric strings (to be used as passwords).
Added some code to VaultDoor, including random password generation and code to encrypt/decrypt under a Vernam-like cypher.
Modified door opening policy to include the possibility of keeping the door open.
2018-01-08 20:40:50 +01:00
simon
9bb0dc9034 Boilerplate code for Vault Door 2018-01-05 20:42:24 -05:00
Dylan Sheehy
1da894959e fixed serialization code for CubotShield 2018-01-05 11:20:52 -06:00
Dylan Sheehy
7e773699b6 Created Shield hardware 2018-01-05 11:04:05 -06:00
Dylan Sheehy
bc29c36204 Merge remote-tracking branch 'upstream/master' 2018-01-05 09:14:18 -06:00
Dylan Sheehy
eb3deba544 Merge branch 'master' of https://github.com/simon987/Much-Assembly-Required 2018-01-03 07:03:56 -06:00
Dylan Sheehy
c7da764505 Added entries to .gitignore to ignore files that vscode's java extension generates 2018-01-02 07:04:14 -06:00
519 changed files with 162580 additions and 6821 deletions

5
.gitattributes vendored Normal file
View File

@@ -0,0 +1,5 @@
src/main/resources/static/css/bootstrap4-neon-glow.min linguist-vendored
src/main/resources/static/css/bootstrap-grid.min linguist-vendored
src/main/resources/static/css/bootstrap-reboot.min linguist-vendored
src/main/resources/static/js/* linguist-vendored
src/main/resources/static/js/ace/* linguist-vendored

13
.gitignore vendored
View File

@@ -1,8 +1,4 @@
plugins/Plugin NPC.jar
plugins/Plugin Misc HW.jar
plugins/Plant.jar
plugins/Cubot.jar
.idea/*
mar.log
mar.log.lck
@@ -13,5 +9,10 @@ plugins/*.jar
save.json
Server/Server.iml
target/*
Server/Server.iml
Server/src/main/java/META-INF/MANIFEST.MF
src/main/java/META-INF/MANIFEST.MF
src/main/resources/static/js/mar.js
.settings
.project
.classpath
# VSCode Workspace
.vscode/

View File

@@ -8,12 +8,7 @@ Here small unordered list of guidelines to read before creating a pull request
- Use java <= 1.8 features
- Please follow [Google's Java style guide](https://google.github.io/styleguide/javaguide.html)
- Constants (e.g. the energy cost of an in-game action) should be loaded from config.properties
- The project is separated into multiple modules, the `Server` module and its plugins. Plugins should
not depend on another plugin to compile or to run.
(e.g. NPC plugin shouldn't import `net.simon987.biomassplugin` )
- Use `Logmanager.LOGGER` to log messages to the console. Use `.fine()` for debugging messages and `.info()` for
- Use `Logmanager.LOGGER` to log messages to the console. Use `.fine()` for debugging messages and `.info()` for
info for more important messages
that are not too frequently used.
- Do not submit a pull request for a new feature that has not been approved
by [simon987](https://github.com/simon987) in an Issue beforehand
- Please state what tests have been performed in the pull request

View File

@@ -1,7 +1,6 @@
FROM alpine:3.7
RUN apk add --no-cache maven openjdk8
FROM maven:3.5-jdk-8
COPY /. /app/
WORKDIR /app
RUN mvn package \
&& cp Server/src/main/resources/config.properties /app/
CMD ["java", "-jar", "/app/target/server-1.2a.jar"]
RUN mvn package
WORKDIR /app/target
CMD ["java", "-jar", "/app/target/server-1.6a.jar"]

View File

@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Server" />
<orderEntry type="library" name="Maven: org.java-websocket:Java-WebSocket:1.3.6" level="project" />
<orderEntry type="library" name="Maven: mysql:mysql-connector-java:5.1.42" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.7" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongo-java-driver:2.10.1" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" name="Maven: junit:junit:4.10" level="project" />
<orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" />
</component>
</module>

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.simon987.server</groupId>
<artifactId>server_root</artifactId>
<version>1.2a</version>
</parent>
<groupId>net.simon987.plugincubot</groupId>
<artifactId>plugin-cubot</artifactId>
<version>1.2a</version>
<dependencies>
<dependency>
<groupId>net.simon987.server</groupId>
<artifactId>server</artifactId>
<version>1.2a</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,308 +0,0 @@
package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.Memory;
import net.simon987.server.game.*;
import net.simon987.server.user.User;
import org.json.simple.JSONObject;
import java.util.ArrayList;
public class Cubot extends GameObject implements Updatable, ControllableUnit, Programmable {
private static final char MAP_INFO = 0x0080;
public static final int ID = 1;
public static int TYPE_ID = 2;
private int hologram = 0;
private String hologramString = "";
private HologramMode hologramMode = HologramMode.CLEARED;
private HologramMode lastHologramMode = HologramMode.CLEARED;
private int hologramColor = 0;
/**
* Hit points
*/
private int hp;
private int heldItem;
private Action currentAction = Action.IDLE;
private Action lastAction = Action.IDLE;
private ArrayList<Integer> keyboardBuffer = new ArrayList<>();
private ArrayList<char[]> consoleMessagesBuffer = new ArrayList<>(CONSOLE_BUFFER_MAX_SIZE);
private ArrayList<char[]> lastConsoleMessagesBuffer = new ArrayList<>(CONSOLE_BUFFER_MAX_SIZE);
private ConsoleMode consoleMode = ConsoleMode.NORMAL;
private ConsoleMode lastConsoleMode = ConsoleMode.NORMAL;
private User parent;
private int energy;
private int maxEnergy;
private static final float SOLAR_PANEL_MULTIPLIER = 1;
private static final int CONSOLE_BUFFER_MAX_SIZE = 40;
public Cubot() {
}
@Override
public char getMapInfo() {
return MAP_INFO;
}
@Override
public void update() {
storeEnergy((int) (SOLAR_PANEL_MULTIPLIER * GameServer.INSTANCE.getDayNightCycle().getSunIntensity()));
if (currentAction == Action.WALKING) {
if (spendEnergy(100)) {
if (!incrementLocation()) {
//Couldn't walk
currentAction = Action.IDLE;
}
} else {
currentAction = Action.IDLE;
}
}
/*
* CurrentAction is set during the code execution and this function is called right after
* If no action as been set, the action sent to the client is the action in currentAction that
* was set last tick (IDLE)
*/
lastAction = currentAction;
currentAction = Action.IDLE;
//Same principle for hologram
lastHologramMode = hologramMode;
hologramMode = HologramMode.CLEARED;
//And the console
lastConsoleMode = consoleMode;
consoleMode = ConsoleMode.NORMAL;
lastConsoleMessagesBuffer = new ArrayList<>(consoleMessagesBuffer);
consoleMessagesBuffer.clear();
}
@Override
public JSONObject serialise() {
JSONObject json = new JSONObject();
json.put("i", getObjectId());
json.put("t", ID);
json.put("x", getX());
json.put("y", getY());
json.put("direction", getDirection().ordinal());
json.put("heldItem", heldItem);
json.put("hp", hp);
json.put("action", lastAction.ordinal());
json.put("holo", hologram);
json.put("holoStr", hologramString);
json.put("holoMode", lastHologramMode.ordinal());
json.put("holoC", hologramColor);
json.put("energy", energy);
if (parent != null) {
json.put("parent", parent.getUsername()); //Only used client-side for now
}
return json;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("i", getObjectId());
dbObject.put("t", ID);
dbObject.put("x", getX());
dbObject.put("y", getY());
dbObject.put("direction", getDirection().ordinal());
dbObject.put("heldItem", heldItem);
dbObject.put("hp", hp);
dbObject.put("action", lastAction.ordinal());
dbObject.put("holo", hologram);
dbObject.put("holoStr", hologramString);
dbObject.put("holoMode", lastHologramMode.ordinal());
dbObject.put("holoC", hologramColor);
dbObject.put("energy", energy);
if (parent != null) {
dbObject.put("parent", parent.getUsername()); //Only used client-side for now
}
return dbObject;
}
public static Cubot deserialize(DBObject obj) {
Cubot cubot = new Cubot();
cubot.setObjectId((long) obj.get("i"));
cubot.setX((int) obj.get("x"));
cubot.setY((int) obj.get("y"));
cubot.hp = (int) obj.get("hp");
cubot.setDirection(Direction.getDirection((int) obj.get("direction")));
cubot.heldItem = (int) obj.get("heldItem");
cubot.energy = (int) obj.get("energy");
cubot.maxEnergy = GameServer.INSTANCE.getConfig().getInt("battery_max_energy");
return cubot;
}
public void setHeldItem(int heldItem) {
this.heldItem = heldItem;
}
public int getHeldItem() {
return heldItem;
}
@Override
public void setKeyboardBuffer(ArrayList<Integer> kbBuffer) {
keyboardBuffer = kbBuffer;
}
@Override
public ArrayList<Integer> getKeyboardBuffer() {
return keyboardBuffer;
}
public void clearKeyboardBuffer() {
keyboardBuffer.clear();
}
public void setCurrentAction(Action currentAction) {
this.currentAction = currentAction;
}
public User getParent() {
return parent;
}
public void setParent(User parent) {
this.parent = parent;
}
public Action getAction() {
return lastAction;
}
public Action getCurrentAction() {
return currentAction;
}
public void setHologram(int hologram) {
this.hologram = hologram;
}
public void setHologramString(String hologramString) {
this.hologramString = hologramString;
}
public int getEnergy() {
return energy;
}
public void setEnergy(int energy) {
this.energy = energy;
}
public boolean spendEnergy(int spent) {
if (energy - spent < 0) {
return false;
} else {
energy -= spent;
return true;
}
}
public void storeEnergy(int qty) {
energy = Math.min(energy + qty, maxEnergy);
}
public void setMaxEnergy(int maxEnergy) {
this.maxEnergy = maxEnergy;
}
public int getMaxEnergy() {
return maxEnergy;
}
@Override
public Memory getFloppyData() {
CubotFloppyDrive drive = ((CubotFloppyDrive) getParent().getCpu().getHardware(CubotFloppyDrive.DEFAULT_ADDRESS));
if (drive.getFloppy() != null) {
return drive.getFloppy().getMemory();
} else {
return null;
}
}
@Override
public boolean isAt(int x, int y) {
return false;
}
public void setHologramMode(HologramMode hologramMode) {
this.hologramMode = hologramMode;
}
public enum HologramMode {
CLEARED,
HEX,
STRING,
DEC
}
public enum ConsoleMode {
CLEAR,
NORMAL
}
@Override
public void setAction(Action action) {
currentAction = action;
}
@Override
public boolean sendMessage(char[] message) {
if (consoleMessagesBuffer.size() < CONSOLE_BUFFER_MAX_SIZE) {
consoleMessagesBuffer.add(message);
return true;
} else {
return false;
}
}
public ArrayList<char[]> getConsoleMessagesBuffer() {
return lastConsoleMessagesBuffer;
}
public int getConsoleMode() {
return lastConsoleMode.ordinal();
}
public void setConsoleMode(ConsoleMode consoleMode) {
this.consoleMode = consoleMode;
}
public void setHologramColor(int hologramColor) {
this.hologramColor = hologramColor;
}
}

View File

@@ -1,63 +0,0 @@
package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status;
public class CubotBattery extends CpuHardware {
public static final int DEFAULT_ADDRESS = 0x000A;
/**
* Hardware ID (Should be unique)
*/
public static final char HWID = 0x000A;
private Cubot cubot;
private static final int BATTERY_POLL = 1;
private static final int BATTERY_GET_MAX_CAPACITY = 2;
public CubotBattery(Cubot cubot) {
this.cubot = cubot;
}
@Override
public void handleInterrupt(Status status) {
int a = getCpu().getRegisterSet().getRegister("A").getValue();
if (a == BATTERY_POLL) {
getCpu().getRegisterSet().getRegister("B").setValue(cubot.getEnergy());
} else if (a == BATTERY_GET_MAX_CAPACITY) {
getCpu().getRegisterSet().getRegister("B").setValue(cubot.getMaxEnergy());
} else if (a == 0xFFFF) {
cubot.setEnergy(cubot.getMaxEnergy());
}
}
@Override
public char getId() {
return HWID;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
public static CubotBattery deserialize(DBObject obj) {
return new CubotBattery((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
}

View File

@@ -1,78 +0,0 @@
package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status;
import net.simon987.server.game.Action;
import net.simon987.server.game.TileMap;
public class CubotDrill extends CpuHardware {
/**
* Hardware ID (Should be unique)
*/
static final char HWID = 0x0005;
public static final int DEFAULT_ADDRESS = 5;
private static final int DRILL_POLL = 1;
private static final int DRILL_GATHER = 2; // simplified gather
private Cubot cubot;
public CubotDrill(Cubot cubot) {
this.cubot = cubot;
}
@Override
public char getId() {
return HWID;
}
@Override
public void handleInterrupt(Status status) {
int a = getCpu().getRegisterSet().getRegister("A").getValue();
if (a == DRILL_POLL) {
getCpu().getRegisterSet().getRegister("B").setValue(0);
} else if (a == DRILL_GATHER) {
if (cubot.spendEnergy(1400)) {
if (cubot.getCurrentAction() == Action.IDLE) {
int tile = cubot.getWorld().getTileMap().getTileAt(cubot.getX(), cubot.getY());
if (tile == TileMap.IRON_TILE) {
cubot.setHeldItem(TileMap.ITEM_IRON);
cubot.setCurrentAction(Action.DIGGING);
} else if (tile == TileMap.COPPER_TILE) {
cubot.setHeldItem(TileMap.ITEM_COPPER);
cubot.setCurrentAction(Action.DIGGING);
}
}
}
}
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
public static CubotDrill deserialize(DBObject obj) {
return new CubotDrill((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
}

View File

@@ -1,101 +0,0 @@
package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status;
public class CubotHologram extends CpuHardware {
/**
* Hardware ID (Should be unique)
*/
static final char HWID = 0x0009;
public static final int DEFAULT_ADDRESS = 9;
private Cubot cubot;
private static final int HOLO_CLEAR = 0;
private static final int HOLO_DISPLAY_HEX = 1;
private static final int HOLO_DISPLAY_STRING = 2;
private static final int HOLO_DISPLAY_DEC = 3;
private static final int HOLO_DISPLAY_COLOR = 4;
private static final int STR_MAX_LEN = 8;
public CubotHologram(Cubot cubot) {
this.cubot = cubot;
}
@Override
public void handleInterrupt(Status status) {
char a = getCpu().getRegisterSet().getRegister("A").getValue();
if (a == HOLO_CLEAR) {
cubot.setHologramMode(Cubot.HologramMode.CLEARED);
} else if (a == HOLO_DISPLAY_HEX) {
char b = getCpu().getRegisterSet().getRegister("B").getValue();
cubot.setHologram(b);
cubot.setHologramMode(Cubot.HologramMode.HEX);
} else if (a == HOLO_DISPLAY_STRING) {
char x = getCpu().getRegisterSet().getRegister("X").getValue();
//Display zero-terminated string starting at X (max 8 chars)
StringBuilder holoString = new StringBuilder();
for (int i = 0; i < STR_MAX_LEN; i++) {
char nextChar = (char) getCpu().getMemory().get(x + i);
if (nextChar != 0) {
holoString.append((char) getCpu().getMemory().get(x + i));
} else {
break;
}
}
cubot.setHologramString(holoString.toString());
cubot.setHologramMode(Cubot.HologramMode.STRING);
} else if (a == HOLO_DISPLAY_DEC) {
//Display decimal number
char b = getCpu().getRegisterSet().getRegister("B").getValue();
cubot.setHologram(b);
cubot.setHologramMode(Cubot.HologramMode.DEC);
} else if (a == HOLO_DISPLAY_COLOR) {
if (cubot.spendEnergy(4)) {
int b = getCpu().getRegisterSet().getRegister("B").getValue();
int c = getCpu().getRegisterSet().getRegister("C").getValue();
cubot.setHologramColor((c | (b << 16))); //B:C
}
}
}
@Override
public char getId() {
return HWID;
}
public static CubotHologram deserialize(DBObject obj) {
return new CubotHologram((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
}

View File

@@ -1,68 +0,0 @@
package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status;
public class CubotInventory extends CpuHardware {
/**
* Hardware ID (Should be unique)
*/
static final char HWID = 0x0006;
public static final int DEFAULT_ADDRESS = 6;
private Cubot cubot;
private static final int INV_CLEAR = 0;
private static final int INV_POLL = 1;
public CubotInventory(Cubot cubot) {
this.cubot = cubot;
}
@Override
public char getId() {
return HWID;
}
@Override
public void handleInterrupt(Status status) {
int a = getCpu().getRegisterSet().getRegister("A").getValue();
if (a == INV_POLL) {
getCpu().getRegisterSet().getRegister("B").setValue(cubot.getHeldItem());
} else if (a == INV_CLEAR) {
if (cubot.getHeldItem() == 0x0001) {
int energy = GameServer.INSTANCE.getConfig().getInt("biomassEnergyValue");
cubot.storeEnergy(energy);
cubot.setHeldItem(0);
} else if (cubot.spendEnergy(100)) {
cubot.setHeldItem(0);
}
}
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
public static CubotInventory deserialize(DBObject obj) {
return new CubotInventory((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
}

View File

@@ -1,105 +0,0 @@
package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status;
import net.simon987.server.game.Action;
import net.simon987.server.game.Attackable;
import net.simon987.server.game.GameObject;
import net.simon987.server.game.InventoryHolder;
import java.awt.*;
import java.util.ArrayList;
public class CubotLaser extends CpuHardware {
/**
* Hardware ID (Should be unique)
*/
static final char HWID = 0x0002;
public static final int DEFAULT_ADDRESS = 2;
private Cubot cubot;
private static final int LASER_WITHDRAW = 1;
private static final int LASER_DEPOSIT = 2;
private static final int LASER_ATTACK = 3;
private static final int LASER_DAMAGE = 25;
public CubotLaser(Cubot cubot) {
this.cubot = cubot;
}
@Override
public char getId() {
return HWID;
}
@Override
public void handleInterrupt(Status status) {
int a = getCpu().getRegisterSet().getRegister("A").getValue();
int b = getCpu().getRegisterSet().getRegister("B").getValue();
if (a == LASER_WITHDRAW) {
Point frontTile = cubot.getFrontTile();
ArrayList<GameObject> objects = cubot.getWorld().getGameObjectsBlockingAt(frontTile.x, frontTile.y);
if (cubot.getCurrentAction() == Action.IDLE && objects.size() > 0) {
//FIXME: Problem here if more than 1 object
if (objects.get(0) instanceof InventoryHolder) {
if (((InventoryHolder) objects.get(0)).canTakeItem(b)) {
if (cubot.spendEnergy(30)) {
//Take the item
((InventoryHolder) objects.get(0)).takeItem(b);
cubot.setHeldItem(b);
cubot.setCurrentAction(Action.WITHDRAWING);
}
}
}
}
} else if (a == LASER_DEPOSIT) {
// TODO
} else if (a == LASER_ATTACK) {
if (cubot.spendEnergy(70)) {
//Get object directly in front of the Cubot
Point frontTile = cubot.getFrontTile();
ArrayList<GameObject> objects = cubot.getWorld().getGameObjectsAt(frontTile.x, frontTile.y);
if (objects.size() > 0 && objects.get(0) instanceof Attackable) {
((Attackable) objects.get(0)).damage(LASER_DAMAGE);
}
}
}
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
public static CubotLaser deserialize(DBObject obj) {
return new CubotLaser((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
}

View File

@@ -1,104 +0,0 @@
package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status;
import net.simon987.server.game.Action;
import net.simon987.server.game.Direction;
import net.simon987.server.io.JSONSerialisable;
import org.json.simple.JSONObject;
public class CubotLeg extends CpuHardware implements JSONSerialisable {
public static final int DEFAULT_ADDRESS = 1;
public static final String NAME = "Cubot Leg";
private static final int LEGS_SET_DIR = 1;
private static final int LEGS_SET_DIR_AND_WALK = 2;
/**
* Hardware ID (Should be unique)
*/
static final char HWID = 0x0001;
private Cubot cubot;
public CubotLeg(Cubot cubot) {
this.cubot = cubot;
}
@Override
public char getId() {
return HWID;
}
@Override
public void handleInterrupt(Status status) {
if (cubot.getCurrentAction() == Action.IDLE) {
int a = getCpu().getRegisterSet().getRegister("A").getValue();
int b = getCpu().getRegisterSet().getRegister("B").getValue();
if (a == LEGS_SET_DIR) {
Direction dir = Direction.getDirection(b);
if (dir != null) {
if (cubot.spendEnergy(20)) {
cubot.setDirection(Direction.getDirection(b));
status.setErrorFlag(false);
}
} else {
status.setErrorFlag(true);
}
} else if (a == LEGS_SET_DIR_AND_WALK) {
if (cubot.getMaxEnergy() >= 100) {
Direction dir = Direction.getDirection(b);
if (dir != null) {
cubot.setDirection(Direction.getDirection(b));
status.setErrorFlag(false);
} else {
status.setErrorFlag(true);
}
cubot.setCurrentAction(Action.WALKING);
}
}
}
}
@Override
public JSONObject serialise() {
JSONObject json = new JSONObject();
json.put("hwid", (int) HWID);
json.put("cubot", cubot.getObjectId());
return json;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
public static CubotLeg deserialize(DBObject obj) {
return new CubotLeg((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
}

View File

@@ -1,67 +0,0 @@
package net.simon987.cubotplugin;
import com.mongodb.DBObject;
import net.simon987.cubotplugin.event.CpuInitialisationListener;
import net.simon987.cubotplugin.event.UserCreationListener;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.game.GameObject;
import net.simon987.server.io.CpuHardwareDeserializer;
import net.simon987.server.io.GameObjectDeserializer;
import net.simon987.server.logging.LogManager;
import net.simon987.server.plugin.ServerPlugin;
public class CubotPlugin extends ServerPlugin implements GameObjectDeserializer, CpuHardwareDeserializer {
@Override
public void init(ServerConfiguration config) {
listeners.add(new CpuInitialisationListener());
listeners.add(new UserCreationListener());
LogManager.LOGGER.info("Initialised Cubot plugin");
}
@Override
public GameObject deserializeObject(DBObject object) {
int objType = (int) object.get("t");
if (objType == Cubot.ID) {
return Cubot.deserialize(object);
}
return null;
}
@Override
public CpuHardware deserializeHardware(DBObject obj) {
int hwid = (int) obj.get("hwid");
switch (hwid) {
case CubotLeg.HWID:
return CubotLeg.deserialize(obj);
case CubotLaser.HWID:
return CubotLaser.deserialize(obj);
case CubotLidar.HWID:
return CubotLidar.deserialize(obj);
case CubotDrill.HWID:
return CubotDrill.deserialize(obj);
case CubotInventory.HWID:
return CubotInventory.deserialize(obj);
case CubotKeyboard.HWID:
return CubotKeyboard.deserialize(obj);
case CubotHologram.HWID:
return CubotHologram.deserialize(obj);
case CubotBattery.HWID:
return CubotBattery.deserialize(obj);
case CubotFloppyDrive.HWID:
return CubotFloppyDrive.deserialize(obj);
case CubotComPort.HWID:
return CubotComPort.deserialize(obj);
}
return null;
}
}

View File

@@ -1,56 +0,0 @@
package net.simon987.cubotplugin.event;
import net.simon987.cubotplugin.*;
import net.simon987.server.assembly.CPU;
import net.simon987.server.event.CpuInitialisationEvent;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.user.User;
public class CpuInitialisationListener implements GameEventListener {
@Override
public Class getListenedEventType() {
return CpuInitialisationEvent.class;
}
@Override
public void handle(GameEvent event) {
//LogManager.LOGGER.fine("(Plugin) Handled CPU Initialisation event (Cubot Plugin)");
CPU cpu = (CPU) event.getSource();
User user = ((CpuInitialisationEvent) event).getUser();
CubotLeg legHw = new CubotLeg((Cubot) user.getControlledUnit());
legHw.setCpu(cpu);
CubotLaser laserHw = new CubotLaser((Cubot) user.getControlledUnit());
laserHw.setCpu(cpu);
CubotLidar radarHw = new CubotLidar((Cubot) user.getControlledUnit());
radarHw.setCpu(cpu);
CubotKeyboard keyboard = new CubotKeyboard((Cubot) user.getControlledUnit());
keyboard.setCpu(cpu);
CubotDrill drillHw = new CubotDrill((Cubot) user.getControlledUnit());
drillHw.setCpu(cpu);
CubotInventory invHw = new CubotInventory((Cubot) user.getControlledUnit());
invHw.setCpu(cpu);
CubotHologram emoteHw = new CubotHologram((Cubot) user.getControlledUnit());
emoteHw.setCpu(cpu);
CubotBattery batteryHw = new CubotBattery((Cubot) user.getControlledUnit());
batteryHw.setCpu(cpu);
CubotFloppyDrive floppyHw = new CubotFloppyDrive((Cubot) user.getControlledUnit());
floppyHw.setCpu(cpu);
CubotComPort comPortHw = new CubotComPort((Cubot) user.getControlledUnit());
comPortHw.setCpu(cpu);
cpu.attachHardware(legHw, CubotLeg.DEFAULT_ADDRESS);
cpu.attachHardware(laserHw, CubotLaser.DEFAULT_ADDRESS);
cpu.attachHardware(radarHw, CubotLidar.DEFAULT_ADDRESS);
cpu.attachHardware(keyboard, CubotKeyboard.DEFAULT_ADDRESS);
cpu.attachHardware(drillHw, CubotDrill.DEFAULT_ADDRESS);
cpu.attachHardware(invHw, CubotInventory.DEFAULT_ADDRESS);
cpu.attachHardware(invHw, CubotInventory.DEFAULT_ADDRESS);
cpu.attachHardware(emoteHw, CubotHologram.DEFAULT_ADDRESS);
cpu.attachHardware(batteryHw, CubotBattery.DEFAULT_ADDRESS);
cpu.attachHardware(floppyHw, CubotFloppyDrive.DEFAULT_ADDRESS);
cpu.attachHardware(comPortHw, CubotComPort.DEFAULT_ADDRESS);
}
}

View File

@@ -1,54 +0,0 @@
package net.simon987.cubotplugin.event;
import net.simon987.cubotplugin.Cubot;
import net.simon987.server.GameServer;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.UserCreationEvent;
import net.simon987.server.logging.LogManager;
import net.simon987.server.user.User;
import java.awt.*;
import java.util.Random;
public class UserCreationListener implements GameEventListener {
@Override
public Class getListenedEventType() {
return UserCreationEvent.class;
}
@Override
public void handle(GameEvent event) {
Random random = new Random();
User user = (User) event.getSource();
Cubot cubot = new Cubot();
cubot.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
Point point = null;
while (point == null || cubot.getWorld() == null) {
int spawnX = GameServer.INSTANCE.getConfig().getInt("new_user_worldX") + random.nextInt(5);
int spawnY = GameServer.INSTANCE.getConfig().getInt("new_user_worldY") + random.nextInt(5);
cubot.setWorld(GameServer.INSTANCE.getGameUniverse().getWorld(spawnX, spawnY, true));
point = cubot.getWorld().getRandomPassableTile();
}
cubot.setX(point.x);
cubot.setY(point.y);
cubot.getWorld().addObject(cubot);
cubot.getWorld().incUpdatable();
cubot.setHeldItem(GameServer.INSTANCE.getConfig().getInt("new_user_item"));
cubot.setEnergy(GameServer.INSTANCE.getConfig().getInt("battery_max_energy"));
cubot.setMaxEnergy(GameServer.INSTANCE.getConfig().getInt("battery_max_energy"));
cubot.setParent(user);
user.setControlledUnit(cubot);
LogManager.LOGGER.fine("(Plugin) Handled User creation event (Cubot Plugin)");
}
}

View File

@@ -1,3 +0,0 @@
classpath=net.simon987.cubotplugin.CubotPlugin
name=Cubot Plugin
version=1.0

View File

@@ -1,19 +0,0 @@
package net.simon987.cubotplugin;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class CubotTest {
@Test
public void test(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
assertEquals(1, 1);
}
}

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Server" />
<orderEntry type="library" name="Maven: org.java-websocket:Java-WebSocket:1.3.6" level="project" />
<orderEntry type="library" name="Maven: mysql:mysql-connector-java:5.1.42" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.7" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongo-java-driver:2.10.1" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" name="Maven: junit:junit:4.10" level="project" />
<orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" />
</component>
</module>

View File

@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.simon987.server</groupId>
<artifactId>server_root</artifactId>
<version>1.2a</version>
</parent>
<groupId>net.simon987.pluginmischw</groupId>
<artifactId>plugin-misc-hw</artifactId>
<version>1.2a</version>
<dependencies>
<dependency>
<groupId>net.simon987.server</groupId>
<artifactId>server</artifactId>
<version>1.2a</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,45 +0,0 @@
package net.simon987.mischwplugin;
import com.mongodb.BasicDBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status;
import net.simon987.server.assembly.Util;
public class Clock extends CpuHardware {
public static final char HWID = 0x0008;
public static final char DEFAULT_ADDRESS = 0x0008;
@Override
public void handleInterrupt(Status status) {
int time = (int) GameServer.INSTANCE.getGameUniverse().getTime();
//Will need to be changed to quadword in about 136 years
getCpu().getRegisterSet().getRegister("B").setValue(Util.getHigherWord(time));
getCpu().getRegisterSet().getRegister("C").setValue(Util.getLowerWord(time));
}
@Override
public char getId() {
return HWID;
}
public static Clock deserialize() {
return new Clock();
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
return dbObject;
}
}

View File

@@ -1,34 +0,0 @@
package net.simon987.mischwplugin;
import com.mongodb.DBObject;
import net.simon987.mischwplugin.event.CpuInitialisationListener;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.io.CpuHardwareDeserializer;
import net.simon987.server.logging.LogManager;
import net.simon987.server.plugin.ServerPlugin;
public class MiscHWPlugin extends ServerPlugin implements CpuHardwareDeserializer {
@Override
public void init(ServerConfiguration config) {
listeners.add(new CpuInitialisationListener());
LogManager.LOGGER.info("Initialised Misc Hardware Plugin");
}
@Override
public CpuHardware deserializeHardware(DBObject hwJson) {
int hwid = (int) hwJson.get("hwid");
switch (hwid) {
case RandomNumberGenerator.HWID:
return RandomNumberGenerator.deserialize();
case Clock.HWID:
return Clock.deserialize();
}
return null;
}
}

View File

@@ -1,46 +0,0 @@
package net.simon987.mischwplugin;
import com.mongodb.BasicDBObject;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status;
import java.util.Random;
public class RandomNumberGenerator extends CpuHardware {
public static final char HWID = 0x0007;
public static final char DEFAULT_ADDRESS = 0x0007;
private Random random;
public RandomNumberGenerator() {
random = new Random();
}
@Override
public void handleInterrupt(Status status) {
getCpu().getRegisterSet().getRegister("B").setValue(random.nextInt(0xFFFF));
}
@Override
public char getId() {
return HWID;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
return dbObject;
}
public static RandomNumberGenerator deserialize() {
return new RandomNumberGenerator();
}
}

View File

@@ -1,30 +0,0 @@
package net.simon987.mischwplugin.event;
import net.simon987.mischwplugin.Clock;
import net.simon987.mischwplugin.RandomNumberGenerator;
import net.simon987.server.assembly.CPU;
import net.simon987.server.event.CpuInitialisationEvent;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
public class CpuInitialisationListener implements GameEventListener {
@Override
public Class getListenedEventType() {
return CpuInitialisationEvent.class;
}
@Override
public void handle(GameEvent event) {
CPU cpu = (CPU) event.getSource();
RandomNumberGenerator rngHW = new RandomNumberGenerator();
rngHW.setCpu(cpu);
Clock clock = new Clock();
clock.setCpu(cpu);
cpu.attachHardware(rngHW, RandomNumberGenerator.DEFAULT_ADDRESS);
cpu.attachHardware(clock, Clock.DEFAULT_ADDRESS);
}
}

View File

@@ -1,3 +0,0 @@
classpath=net.simon987.mischwplugin.MiscHWPlugin
name=Misc HW Plugin
version=1.0

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" name="Maven: junit:junit:4.10" level="project" />
<orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" />
<orderEntry type="module" module-name="Server" />
<orderEntry type="library" name="Maven: org.java-websocket:Java-WebSocket:1.3.6" level="project" />
<orderEntry type="library" name="Maven: mysql:mysql-connector-java:5.1.42" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.7" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongo-java-driver:2.10.1" level="project" />
</component>
</module>

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.simon987.server</groupId>
<artifactId>server_root</artifactId>
<version>1.2a</version>
</parent>
<groupId>net.simon987.pluginnpc</groupId>
<artifactId>plugin-npc</artifactId>
<version>1.2a</version>
<dependencies>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>net.simon987.server</groupId>
<artifactId>server</artifactId>
<version>1.2a</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,210 +0,0 @@
package net.simon987.npcplugin;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.game.GameObject;
import net.simon987.server.game.Updatable;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.awt.*;
import java.util.ArrayList;
public class Factory extends GameObject implements Updatable {
private static final int MAP_INFO = 0x0200;
static final int ID = 3;
private static final int MAX_NPC_COUNT = GameServer.INSTANCE.getConfig().getInt("factory_max_npc_count");
private static final int NPC_CREATION_COOLDOWN = NonPlayerCharacter.LIFETIME / MAX_NPC_COUNT;
private ArrayList<NonPlayerCharacter> npcs = new ArrayList<>();
/**
* Number of ticks to wait until the Factory can spawn a new NPC
*/
private int cooldown = 0;
/**
* Temporary NPC objectId array. The Factory links the NPCs to itself when initialised,
* at the first call of update().
*/
private Object[] tmpNpcArray = new Object[0];
/**
* Factory are uninitialised until the first update() call
*/
private boolean initialised = false;
@Override
public char getMapInfo() {
return MAP_INFO;
}
@Override
public void update() {
if (!initialised) {
initialised = true;
for (Object id : tmpNpcArray) {
NonPlayerCharacter npc = (NonPlayerCharacter) GameServer.INSTANCE.getGameUniverse().getObject((int) (long) id);
if (npc != null) {
npc.setFactory(this);
npcs.add(npc);
}
}
} else {
if (cooldown == 0) {
if (npcs.size() < MAX_NPC_COUNT) {
Point p = getAdjacentTile();
if (p != null) {
NonPlayerCharacter npc = new HarvesterNPC();
npc.setWorld(getWorld());
npc.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
npc.setX(p.x);
npc.setY(p.y);
getWorld().addObject(npc);
getWorld().incUpdatable();
npc.setFactory(this);
npcs.add(npc);
}
}
cooldown += NPC_CREATION_COOLDOWN;
} else {
cooldown--;
}
}
}
@Override
public boolean isAt(int x, int y) {
/*
* Object is 2x2 tiles, the (x,y) coordinates of the object being
* at top-left.
* # .
* . .
*/
return (x == getX() + 1 || x == getX()) && (y == getY() + 1 || y == getY());
}
@Override
public JSONObject serialise() {
JSONObject json = new JSONObject();
json.put("i", getObjectId());
json.put("x", getX());
json.put("y", getY());
json.put("t", ID);
JSONArray tmpNpcArray = new JSONArray();
for (NonPlayerCharacter npc : npcs) {
tmpNpcArray.add(npc.getObjectId());
}
json.put("n", tmpNpcArray);
return json;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("i", getObjectId());
dbObject.put("x", getX());
dbObject.put("y", getY());
dbObject.put("t", ID);
BasicDBList tmpNpcArray = new BasicDBList();
for (NonPlayerCharacter npc : npcs) {
tmpNpcArray.add(npc.getObjectId());
}
dbObject.put("n", tmpNpcArray);
return dbObject;
}
public static Factory deserialise(DBObject obj) {
Factory factory = new Factory();
factory.setObjectId((long) obj.get("i"));
factory.setX((int) obj.get("x"));
factory.setY((int) obj.get("y"));
factory.tmpNpcArray = ((BasicDBList) obj.get("n")).toArray();
return factory;
}
/**
* Get the first non-blocked tile that is directly adjacent to the factory, starting from the north-east corner
* going clockwise.
*
* @return The coordinates of the first non-blocked tile, null otherwise.
*/
public Point getAdjacentTile() {
/*
* (2,0)
* (2,1)
* (1,2)
* (0,2)
* (-1,1)
* (-1,0)
* (0,-1)
* (1,-1)
*/
if (!getWorld().isTileBlocked(getX() + 2, getY())) {
return new Point(getX() + 2, getY());
} else if (!getWorld().isTileBlocked(getX() + 2, getY() + 1)) {
return new Point(getX() + 2, getY() + 1);
} else if (!getWorld().isTileBlocked(getX() + 1, getY() + 2)) {
return new Point(getX() + 1, getY() + 2);
} else if (!getWorld().isTileBlocked(getX(), getY() + 2)) {
return new Point(getX(), getY() + 2);
} else if (!getWorld().isTileBlocked(getX() + -1, getY() + 1)) {
return new Point(getX() + -1, getY() + 1);
} else if (!getWorld().isTileBlocked(getX() + -1, getY())) {
return new Point(getX() + -1, getY());
} else if (!getWorld().isTileBlocked(getX(), getY() + -1)) {
return new Point(getX(), getY() + -1);
} else if (!getWorld().isTileBlocked(getX() + 1, getY() + -1)) {
return new Point(getX() + 1, getY() + -1);
} else {
return null;
}
}
ArrayList<NonPlayerCharacter> getNpcs() {
return npcs;
}
}

View File

@@ -1,105 +0,0 @@
package net.simon987.npcplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.event.ObjectDeathEvent;
import net.simon987.server.game.Direction;
import org.json.simple.JSONObject;
public class HarvesterNPC extends NonPlayerCharacter {
public static final int ID = 10;
public static final int MAX_HEALTH = GameServer.INSTANCE.getConfig().getInt("harvester_hp_max");
public static final int HEAL_RATE = GameServer.INSTANCE.getConfig().getInt("harvester_regen");
public HarvesterNPC() {
setTask(new HarvestTask());
setHp(MAX_HEALTH);
setMaxHp(MAX_HEALTH);
setHealRate(HEAL_RATE);
}
@Override
public void update() {
super.update();
if (getFactory() != null) {
if (getTask().checkCompleted()) {
setTask(new HarvestTask());
} else {
getTask().tick(this);
}
//Self-destroy when age limit is reached
if (getAge() >= NonPlayerCharacter.LIFETIME) {
setDead(true);
}
}
}
@Override
public void onDeadCallback() {
if (getFactory() != null && getFactory().getNpcs() != null) {
getFactory().getNpcs().remove(this);
}
GameServer.INSTANCE.getEventDispatcher().dispatch(
new ObjectDeathEvent(this, ID));
}
@Override
public JSONObject serialise() {
JSONObject json = super.serialise();
json.put("i", getObjectId());
json.put("x", getX());
json.put("y", getY());
json.put("direction", getDirection().ordinal());
json.put("hp", getHp());
json.put("energy", energy);
json.put("action", getAction().ordinal());
json.put("t", ID);
return json;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("i", getObjectId());
dbObject.put("x", getX());
dbObject.put("y", getY());
dbObject.put("direction", getDirection().ordinal());
dbObject.put("hp", getHp());
// dbObject.put("energy", energy);
dbObject.put("action", getAction().ordinal());
dbObject.put("t", ID);
return dbObject;
}
public static HarvesterNPC deserialize(DBObject obj) {
HarvesterNPC npc = new HarvesterNPC();
npc.setObjectId((long) obj.get("i"));
npc.setX((int) obj.get("x"));
npc.setY((int) obj.get("y"));
npc.setHp((int) obj.get("hp"));
npc.setDirection(Direction.getDirection((int) obj.get("direction")));
// npc.energy = (int) obj.get("energy");
// npc.maxEnergy = GameServer.INSTANCE.getConfig().getInt("battery_max_energy");
return npc;
}
}

View File

@@ -1,65 +0,0 @@
package net.simon987.npcplugin;
import com.mongodb.DBObject;
import net.simon987.npcplugin.event.CpuInitialisationListener;
import net.simon987.npcplugin.event.WorldCreationListener;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.game.GameObject;
import net.simon987.server.io.CpuHardwareDeserializer;
import net.simon987.server.io.GameObjectDeserializer;
import net.simon987.server.logging.LogManager;
import net.simon987.server.plugin.ServerPlugin;
import java.util.ArrayList;
public class NpcPlugin extends ServerPlugin implements GameObjectDeserializer, CpuHardwareDeserializer {
/**
* Radio tower cache
*/
private static ArrayList<RadioTower> radioTowers;
@Override
public void init(ServerConfiguration configuration) {
listeners.add(new WorldCreationListener());
listeners.add(new CpuInitialisationListener());
radioTowers = new ArrayList<>(32);
LogManager.LOGGER.info("Initialised NPC plugin");
}
@Override
public GameObject deserializeObject(DBObject obj) {
int objType = (int) obj.get("t");
if (objType == HarvesterNPC.ID) {
return HarvesterNPC.deserialize(obj);
} else if (objType == Factory.ID) {
return Factory.deserialise(obj);
} else if (objType == RadioTower.ID) {
return RadioTower.deserialize(obj);
}
return null;
}
@Override
public CpuHardware deserializeHardware(DBObject obj) {
int hwid = (int) obj.get("hwid");
switch (hwid) {
case RadioReceiverHardware.HWID:
return RadioReceiverHardware.deserialize(obj);
}
return null;
}
public static ArrayList<RadioTower> getRadioTowers() {
return radioTowers;
}
}

View File

@@ -1,120 +0,0 @@
package net.simon987.npcplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.game.GameObject;
import net.simon987.server.game.Programmable;
import net.simon987.server.game.Updatable;
import org.json.simple.JSONObject;
import java.awt.*;
import java.util.ArrayList;
public class RadioTower extends GameObject implements Programmable, Updatable {
private static final int MAP_INFO = 0x1000;
public static final int ID = 4;
public static final int MAX_RANGE = 3; //todo load from config
private static final int MAX_MESSAGES = 16;
@Override
public char getMapInfo() {
return MAP_INFO;
}
/**
* Messages from the current tick
*/
private ArrayList<char[]> messages = new ArrayList<>(4);
/**
* Messages from the last tick
*/
private ArrayList<char[]> lastMessages = new ArrayList<>(4);
@Override
public void update() {
lastMessages = new ArrayList<>(messages);
messages.clear();
}
@Override
public boolean sendMessage(char[] message) {
if (message.length < MAX_MESSAGES) {
messages.add(message);
return true;
} else {
return false;
}
}
@Override
public JSONObject serialise() {
JSONObject json = new JSONObject();
json.put("i", getObjectId());
json.put("x", getX());
json.put("y", getY());
json.put("t", ID);
return json;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("i", getObjectId());
dbObject.put("x", getX());
dbObject.put("y", getY());
dbObject.put("t", ID);
return dbObject;
}
public static RadioTower deserialize(DBObject obj) {
RadioTower tower = new RadioTower();
tower.setObjectId((long) obj.get("i"));
tower.setX((int) obj.get("x"));
tower.setY((int) obj.get("y"));
NpcPlugin.getRadioTowers().add(tower);
return tower;
}
public ArrayList<char[]> getMessages() {
return lastMessages;
}
/**
* Get the first directly adjacent tile (starting east, going clockwise)
*/
public Point getAdjacentTile() {
if (!getWorld().isTileBlocked(getX() + 1, getY())) {
return new Point(getX() + 1, getY());
} else if (!getWorld().isTileBlocked(getX(), getY() + 1)) {
return new Point(getX(), getY() + 1);
} else if (!getWorld().isTileBlocked(getX() - 1, getY())) {
return new Point(getX() - 1, getY());
} else if (!getWorld().isTileBlocked(getX(), getY() - 1)) {
return new Point(getX(), getY() - 1);
} else {
return null;
}
}
}

View File

@@ -1,27 +0,0 @@
package net.simon987.npcplugin.event;
import net.simon987.npcplugin.RadioReceiverHardware;
import net.simon987.server.assembly.CPU;
import net.simon987.server.event.CpuInitialisationEvent;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.user.User;
public class CpuInitialisationListener implements GameEventListener {
@Override
public Class getListenedEventType() {
return CpuInitialisationEvent.class;
}
@Override
public void handle(GameEvent event) {
CPU cpu = (CPU) event.getSource();
User user = ((CpuInitialisationEvent) event).getUser();
RadioReceiverHardware radioHw = new RadioReceiverHardware(user.getControlledUnit());
radioHw.setCpu(cpu);
cpu.attachHardware(radioHw, RadioReceiverHardware.DEFAULT_ADDRESS);
}
}

View File

@@ -1,98 +0,0 @@
package net.simon987.npcplugin.event;
import net.simon987.npcplugin.Factory;
import net.simon987.npcplugin.NpcPlugin;
import net.simon987.npcplugin.RadioTower;
import net.simon987.server.GameServer;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.WorldGenerationEvent;
import net.simon987.server.game.World;
import net.simon987.server.logging.LogManager;
import java.awt.*;
import java.util.Random;
public class WorldCreationListener implements GameEventListener {
/**
* Spawn rate. Higher = rarer: A factory will be spawn about every FACTORY_SPAWN_RATE generated Worlds
*/
private static final int FACTORY_SPAWN_RATE = 35;
private Random random = new Random();
@Override
public Class getListenedEventType() {
return WorldGenerationEvent.class;
}
@Override
public void handle(GameEvent event) {
if (random.nextInt(FACTORY_SPAWN_RATE) == 0) {
World world = ((WorldGenerationEvent) event).getWorld();
outerLoopFactory:
for (int x = 2; x < 12; x++) {
for (int y = 2; y < 12; y++) {
if ((!world.isTileBlocked(x, y) && !world.isTileBlocked(x + 1, y) &&
!world.isTileBlocked(x, y + 1) && !world.isTileBlocked(x + 1, y + 1))) {
Factory factory = new Factory();
factory.setWorld(world);
factory.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
factory.setX(x);
factory.setY(y);
if (factory.getAdjacentTile() == null) {
//Factory has no non-blocked adjacent tiles
continue;
}
world.addObject(factory);
world.incUpdatable();
LogManager.LOGGER.info("Spawned Factory at (" + world.getX() + ", " + world.getY() +
") (" + x + ", " + y + ")");
break outerLoopFactory;
}
}
}
//Also spawn a radio tower in the same World
Point p = world.getRandomPassableTile();
if (p != null) {
while (p.x == 0 || p.x == World.WORLD_SIZE - 1 || p.y == World.WORLD_SIZE - 1 || p.y == 0) {
p = world.getRandomPassableTile();
if (p == null) {
//World is full
return;
}
}
RadioTower radioTower = new RadioTower();
radioTower.setWorld(world);
radioTower.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
radioTower.setX(p.x);
radioTower.setY(p.y);
if (radioTower.getAdjacentTile() != null) {
//Radio Tower has adjacent tiles
world.addObject(radioTower);
world.incUpdatable(); //In case the Factory couldn't be spawned.
NpcPlugin.getRadioTowers().add(radioTower);
LogManager.LOGGER.info("Spawned RadioTower at (" + world.getX() + ", " + world.getY() +
") (" + p.x + ", " + p.y + ")");
}
}
}
}
}

View File

@@ -1,3 +0,0 @@
classpath=net.simon987.npcplugin.NpcPlugin
name=NPC Plugin
version=1.0

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" name="Maven: junit:junit:4.10" level="project" />
<orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" />
<orderEntry type="module" module-name="Server" />
<orderEntry type="library" name="Maven: org.java-websocket:Java-WebSocket:1.3.6" level="project" />
<orderEntry type="library" name="Maven: mysql:mysql-connector-java:5.1.42" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.7" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongo-java-driver:2.10.1" level="project" />
</component>
</module>

View File

@@ -1,32 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.simon987.server</groupId>
<artifactId>server_root</artifactId>
<version>1.2a</version>
</parent>
<groupId>net.simon987.pluginplant</groupId>
<artifactId>plugin-biomassBlob</artifactId>
<version>1.2a</version>
<dependencies>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>net.simon987.server</groupId>
<artifactId>server</artifactId>
<version>1.2a</version>
</dependency>
</dependencies>
</project>

View File

@@ -1,126 +0,0 @@
package net.simon987.biomassplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.game.GameObject;
import net.simon987.server.game.InventoryHolder;
import org.json.simple.JSONObject;
public class BiomassBlob extends GameObject implements InventoryHolder {
private static final char MAP_INFO = 0x4000;
public static final int ID = 2;
/**
* Yield of the blob, in biomass units
*/
private int biomassCount;
/**
* Style of the blob (Only visual)
*/
// private int style;
private static final int ITM_BIOMASS = 1;
@Override
public char getMapInfo() {
return MAP_INFO;
}
@Override
public JSONObject serialise() {
JSONObject json = new JSONObject();
json.put("t", ID);
json.put("i", getObjectId());
json.put("x", getX());
json.put("y", getY());
json.put("b", biomassCount);
// json.put("style", style);
return json;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("t", ID);
dbObject.put("i", getObjectId());
dbObject.put("x", getX());
dbObject.put("y", getY());
dbObject.put("b", biomassCount);
return dbObject;
}
public int getBiomassCount() {
return biomassCount;
}
public void setBiomassCount(int biomassCount) {
this.biomassCount = biomassCount;
}
// public int getStyle() {
// return style;
// }
//
// public void setStyle(int style) {
// this.style = style;
// }
public static BiomassBlob deserialize(DBObject obj) {
BiomassBlob biomassBlob = new BiomassBlob();
biomassBlob.setObjectId((long) obj.get("i"));
biomassBlob.setX((int) obj.get("x"));
biomassBlob.setY((int) obj.get("y"));
// biomassBlob.style = (int) json.get("style");
biomassBlob.biomassCount = (int) obj.get("b");
return biomassBlob;
}
/**
* Called when an object attempts to place an item in this BiomassBlob
*
* @param item item id (see MarConstants.ITEM_*)
* @return Always returns false
*/
@Override
public boolean placeItem(int item) {
//Why would you want to place an item in a blob?
return false;
}
@Override
public boolean canTakeItem(int item) {
return item == ITM_BIOMASS && biomassCount >= 1;
}
/**
* Called when an object attempts to take an item from this BiomassBlob.
* If the object requests biomass, it will be subtracted from biomassCount, and
* if it reaches 0, the plant is deleted
*
* @param item item id (see MarConstants.ITEM_*)
*/
@Override
public void takeItem(int item) {
if (item == ITM_BIOMASS) {
if (biomassCount > 1) {
biomassCount--;
} else {
//Delete plant
setDead(true);
}
}
}
}

View File

@@ -1,37 +0,0 @@
package net.simon987.biomassplugin;
import com.mongodb.DBObject;
import net.simon987.biomassplugin.event.ObjectDeathListener;
import net.simon987.biomassplugin.event.WorldCreationListener;
import net.simon987.biomassplugin.event.WorldUpdateListener;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.game.GameObject;
import net.simon987.server.io.GameObjectDeserializer;
import net.simon987.server.logging.LogManager;
import net.simon987.server.plugin.ServerPlugin;
public class BiomassPlugin extends ServerPlugin implements GameObjectDeserializer {
@Override
public void init(ServerConfiguration config) {
listeners.add(new WorldCreationListener());
listeners.add(new WorldUpdateListener(config));
listeners.add(new ObjectDeathListener(config));
LogManager.LOGGER.info("Initialised Biomass plugin");
}
@Override
public GameObject deserializeObject(DBObject object) {
int objType = (int) object.get("t");
if (objType == BiomassBlob.ID) {
return BiomassBlob.deserialize(object);
}
return null;
}
}

View File

@@ -1,74 +0,0 @@
package net.simon987.biomassplugin.event;
import net.simon987.biomassplugin.BiomassBlob;
import net.simon987.biomassplugin.WorldUtils;
import net.simon987.server.GameServer;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.WorldUpdateEvent;
import net.simon987.server.game.World;
import java.util.ArrayList;
import java.util.HashMap;
public class WorldUpdateListener implements GameEventListener {
private HashMap<World, Long> worldWaitMap = new HashMap<>(200);
private static int minBlobCount;
private static int maxBlobCount;
private static int blobYield;
private static int waitTime;
private static int blobThreshold;
public WorldUpdateListener(ServerConfiguration config) {
minBlobCount = config.getInt("minBiomassRespawnCount");
maxBlobCount = config.getInt("maxBiomassRespawnCount");
waitTime = config.getInt("biomassRespawnTime");
blobThreshold = config.getInt("biomassRespawnThreshold");
blobYield = config.getInt("biomass_yield");
}
@Override
public Class getListenedEventType() {
return WorldUpdateEvent.class;
}
@Override
public void handle(GameEvent event) {
World world = ((WorldUpdateEvent) event).getWorld();
//If there is less than the respawn threshold,
if (world.findObjects(BiomassBlob.class).size() < blobThreshold) {
//Set a timer for respawn_time ticks
if (!worldWaitMap.containsKey(world) || worldWaitMap.get(world) == 0L) {
worldWaitMap.put(world, GameServer.INSTANCE.getGameUniverse().getTime() + waitTime);
} else {
long waitUntil = worldWaitMap.get(world);
if (GameServer.INSTANCE.getGameUniverse().getTime() >= waitUntil) {
//If the timer was set less than respawn_time ticks ago, respawn the blobs
ArrayList<BiomassBlob> newBlobs = WorldUtils.generateBlobs(world, minBlobCount,
maxBlobCount, blobYield);
for (BiomassBlob blob : newBlobs) {
world.addObject(blob);
}
//Set the 'waitUntil' time to 0 to indicate that we are not waiting
worldWaitMap.replace(world, 0L);
}
}
}
}
}

View File

@@ -1,3 +0,0 @@
classpath=net.simon987.biomassplugin.BiomassPlugin
name=Biomass Plugin
version=1.0

114
README.md
View File

@@ -1,26 +1,42 @@
# [Live demo](https://muchassemblyrequired.com)
### [Official website](https://muchassemblyrequired.com)
[![CodeFactor](https://www.codefactor.io/repository/github/simon987/much-assembly-required/badge)](https://www.codefactor.io/repository/github/simon987/much-assembly-required)
[![Build Status](https://ci.simon987.net/buildStatus/icon?job=Much-Assembly-Required)](https://ci.simon987.net/job/Much-Assembly-Required/)
Program the 8086-like microprocessor of a robot in a grid-based multiplayer world. The game is web based so no installation is required.
In its current state, players can walk around the game universe and collect Biomass blobs & Iron/copper ore using the online code editor.
![screenshot from 2017-11-12 13-01-43](https://user-images.githubusercontent.com/7120851/32701793-e5d07e98-c7a9-11e7-9931-f8db7b287994.png)
Wiki: [GitHub](https://github.com/simon987/Much-Assembly-Required/wiki)
Chat: [Slack](https://join.slack.com/t/muchassemblyrequired/shared_invite/enQtMjY3Mjc1OTUwNjEwLTkyOTIwOTA5OGY4MDVlMGI4NzM5YzlhMWJiMGY1OWE2NjUxODQ1NWQ1YTcxMTA1NGZkYzNjYzMyM2E1ODdmNzg)
## VS Code Extensions
- [Much Assembly Required (Upload on Save)](https://marketplace.visualstudio.com/items?itemName=tomhodder.much-assembly-required-upload-on-save) by tomhodder
- [Much Assembly Required Language Support](https://marketplace.visualstudio.com/items?itemName=PJB3005.much-assembly-required-language-support) by PJB3005
Chat: [Slack](https://join.slack.com/t/muchassemblyrequired/shared_invite/enQtMjY3Mjc1OTUwNjEwLWRjMjRkZTg2N2EyNWRjN2YyMDc0YzIyMTUyYzFiNTBmMTU3OGQ1ZjA0MWY0M2IyYjUxZTA4NjRkMWVkNDk2NzY)
# Deploying the server
Note: You can find the frontend [here](https://github.com/simon987/Much-Assembly-Required-Frontend)
## Linux
**Installing tools**
## Linux (Ubuntu 16.04)
On Ubuntu 16.04:
```bash
# Install tools
sudo apt install git maven openjdk-8-jdk
sudo apt install git maven openjdk-8-jdk mongodb
sudo npm install -g typescript
```
On Arch:
``` bash
sudo pacman -S git maven mongodb jdk8-opendjk
sudo npm install -g typescript
# Don't forget to start mongodb
sudo systemctl start mongodb.service
```
*If needed, visit [troubleshooting mongodb](https://wiki.archlinux.org/index.php/MongoDB#Troubleshooting).*
**Deploying server**
``` bash
# Obtain source files
git clone https://github.com/simon987/Much-Assembly-Required.git
@@ -30,7 +46,7 @@ mvn package
# Run
cd target
java -jar server-1.2a.jar
java -jar muchassemblyrequired-*.jar
```
## Windows (tested on Windows 10)
@@ -61,10 +77,69 @@ mongod
```batch
:: Runs the MAR server
cd Much-Assembly-Required\target
java -jar server-1.2a.jar
java -jar muchassemblyrequired-*.jar
```
3. Run the frontend, following the instructions that you can find [here](https://github.com/simon987/Much-Assembly-Required-Frontend).
## macOS (tested on Sierra 10.12.6)
**Installation**
1. Install [Maven3](https://maven.apache.org/)
-Add Maven bin to your path
```bash
export PATH=/path/to/maven/bin.:$PATH
```
2. Install [MongoDB](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/?_ga=2.201359831.774868398.1539369140-197602459.1539369140).
-Via Brew:
```bash
#Update brew
brew update
#Install mongodb
brew install mongodb
#Install latest development release
brew install mongodb --devel
```
-Via .tar.gz
```bash
#Extract files:
tar -zxvf mongodb-osx-ssl-x86_64-4.0.3.tgz
#Ensure binaries are in your path
export PATH=<mongodb-install-directory>/bin:$PATH
```
If you do not wish to use the default data directory (/data/db), follow the steps for running MongoDB in the install doc.
**Deploying Server**
1. Begin MongoDB service
```bash
#If brew:
#Launch on login
brew services start mongodb
#Or, if you don't want/need a background service you can just run:
mongod --config /usr/local/etc/mongod.conf
#If binary:
mongod
#Optional, set data directory path:
mongod --dbpath <path to data directory>
```
2. Deploy server:
```bash
# Obtain source files
git clone https://github.com/simon987/Much-Assembly-Required.git
# Build
cd Much-Assembly-Required
mvn package
# Run
cd target
java -jar muchassemblyrequired-*.jar
```
## Docker
### Requirements
@@ -79,8 +154,13 @@ application's directory:
`docker-compose up`
This will start MySQL and then build and run this application. It will
be available via http://localhost.
Make sure to change `mongo_address` in `config.properties` to `mongodb`.
Note that there is currently no frontend web application serving the
WebSocket feed served by the `Server` application!
# Running
Once the server is running, you should be able to connect to `http://localhost:4567` with your browser
## VS Code Extensions
- [Much Assembly Required (Upload on Save)](https://marketplace.visualstudio.com/items?itemName=tomhodder.much-assembly-required-upload-on-save) by tomhodder
- [Much Assembly Required Language Support](https://marketplace.visualstudio.com/items?itemName=PJB3005.much-assembly-required-language-support) by PJB3005

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.java-websocket:Java-WebSocket:1.3.6" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="library" name="Maven: mysql:mysql-connector-java:5.1.42" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.7" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongo-java-driver:2.10.1" level="project" />
</component>
</module>

View File

@@ -1,134 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>default-resources</id>
<!-- here the phase you need -->
<phase>prepare-package</phase>
<goals>
<goal>resources</goal>
</goals>
<configuration>
<outputDirectory>../target/</outputDirectory>
<resources>
<resource>
<directory>../Server/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>config.properties</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<version>3.6.2</version>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>../target/libs</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<outputDirectory>../target</outputDirectory>
<archive>
<manifest>
<mainClass>net.simon987.server.Main</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>libs/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.7.2</version>
<configuration>
<workingDirectory>./src/main/resources</workingDirectory>
</configuration>
</plugin>
</plugins>
</build>
<groupId>net.simon987.server</groupId>
<artifactId>server</artifactId>
<version>1.2a</version>
<dependencies>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.3.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>2.10.1</version>
</dependency>
</dependencies>
<properties>
<!-- explicitly set build encoding so not altered by build platform defaults -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -1,277 +0,0 @@
package net.simon987.server;
import com.mongodb.*;
import net.simon987.server.assembly.exception.CancelledException;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventDispatcher;
import net.simon987.server.event.TickEvent;
import net.simon987.server.game.DayNightCycle;
import net.simon987.server.game.GameUniverse;
import net.simon987.server.game.World;
import net.simon987.server.logging.LogManager;
import net.simon987.server.plugin.PluginManager;
import net.simon987.server.user.User;
import net.simon987.server.webserver.SocketServer;
import java.io.File;
import java.net.UnknownHostException;
public class GameServer implements Runnable {
public final static GameServer INSTANCE = new GameServer();
private GameUniverse gameUniverse;
private GameEventDispatcher eventDispatcher;
private PluginManager pluginManager;
private ServerConfiguration config;
private SocketServer socketServer;
private int maxExecutionTime;
private DayNightCycle dayNightCycle;
private MongoClient mongo = null;
public GameServer() {
this.config = new ServerConfiguration("config.properties");
try{
mongo = new MongoClient("localhost", 27017);
} catch (UnknownHostException e) {
e.printStackTrace();
}
gameUniverse = new GameUniverse(config);
gameUniverse.setMongo(mongo);
pluginManager = new PluginManager();
maxExecutionTime = config.getInt("user_timeout");
dayNightCycle = new DayNightCycle();
//Load all plugins in plugins folder, if it doesn't exist, create it
File pluginDir = new File("plugins/");
File[] pluginDirListing = pluginDir.listFiles();
if (pluginDirListing != null) {
for (File pluginFile : pluginDirListing) {
if (pluginFile.getName().endsWith(".jar")) {
pluginManager.load(pluginFile, config);
}
}
} else {
if (!pluginDir.mkdir()) {
LogManager.LOGGER.severe("Couldn't create plugin directory");
}
}
eventDispatcher = new GameEventDispatcher(pluginManager);
eventDispatcher.getListeners().add(dayNightCycle);
}
public GameUniverse getGameUniverse() {
return gameUniverse;
}
public GameEventDispatcher getEventDispatcher() {
return eventDispatcher;
}
@Override
public void run() {
LogManager.LOGGER.info("(G) Started game loop");
long startTime; //Start time of the loop
long uTime; //update time
long waitTime; //time to wait
boolean running = true;
while (running) {
startTime = System.currentTimeMillis();
tick();
uTime = System.currentTimeMillis() - startTime;
waitTime = config.getInt("tick_length") - uTime;
LogManager.LOGGER.info("Wait time : " + waitTime + "ms | Update time: " + uTime + "ms | " + (int) (((double) uTime / waitTime) * 100) + "% load");
try {
if (waitTime >= 0) {
Thread.sleep(waitTime);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void tick() {
gameUniverse.incrementTime();
//Dispatch tick event
GameEvent event = new TickEvent(gameUniverse.getTime());
eventDispatcher.dispatch(event); //Ignore cancellation
//Process user code
for (User user : gameUniverse.getUsers()) {
if (user.getCpu() != null) {
try {
int timeout = Math.min(user.getControlledUnit().getEnergy(), maxExecutionTime);
user.getCpu().reset();
int cost = user.getCpu().execute(timeout);
user.getControlledUnit().spendEnergy(cost);
} catch (Exception e) {
LogManager.LOGGER.severe("Error executing " + user.getUsername() + "'s code");
e.printStackTrace();
}
}
}
//Process each worlds
int updatedWorlds = 0;
for (World world : gameUniverse.getWorlds()) {
if (world.shouldUpdate()) {
world.update();
updatedWorlds++;
}
}
//Save
if (gameUniverse.getTime() % config.getInt("save_interval") == 0) {
save();
}
socketServer.tick();
LogManager.LOGGER.info("Processed " + gameUniverse.getWorldCount() + " worlds (" + updatedWorlds +
") updated");
}
void load() {
LogManager.LOGGER.info("Loading all data from MongoDB");
DB db = mongo.getDB("mar");
DBCollection worlds = db.getCollection("world");
DBCollection users = db.getCollection("user");
DBCollection server = db.getCollection("server");
BasicDBObject whereQuery = new BasicDBObject();
whereQuery.put("shouldUpdate", true);
DBCursor cursor = worlds.find(whereQuery);
GameUniverse universe = GameServer.INSTANCE.getGameUniverse();
while (cursor.hasNext()) {
World w = World.deserialize(cursor.next());
universe.addWorld(w);
}
//Load users
cursor = users.find();
while (cursor.hasNext()) {
try {
universe.addUser(User.deserialize(cursor.next()));
} catch (CancelledException e) {
e.printStackTrace();
}
}
//Load misc server info
cursor = server.find();
if (cursor.hasNext()) {
DBObject serverObj = cursor.next();
gameUniverse.setTime((long) serverObj.get("time"));
gameUniverse.setNextObjectId((long) serverObj.get("nextObjectId"));
}
LogManager.LOGGER.info("Done loading! W:" + GameServer.INSTANCE.getGameUniverse().getWorldCount() +
" | U:" + GameServer.INSTANCE.getGameUniverse().getUserCount());
}
private void save() {
LogManager.LOGGER.info("Saving to MongoDB | W:" + gameUniverse.getWorldCount() + " | U:" + gameUniverse.getUserCount());
try{
DB db = mongo.getDB("mar");
int unloaded_worlds = 0;
DBCollection worlds = db.getCollection("world");
DBCollection users = db.getCollection("user");
DBCollection server = db.getCollection("server");
int insertedWorlds = 0;
GameUniverse universe = GameServer.INSTANCE.getGameUniverse();
for (World w : universe.getWorlds()) {
// LogManager.LOGGER.fine("Saving world "+w.getId()+" to mongodb");
insertedWorlds++;
worlds.save(w.mongoSerialise());
// If the world should unload, it is removed from the Universe after having been saved.
if (w.shouldUnload()){
unloaded_worlds++;
// LogManager.LOGGER.fine("Unloading world "+w.getId()+" from universe");
universe.removeWorld(w);
}
}
for (User u : GameServer.INSTANCE.getGameUniverse().getUsers()) {
if (!u.isGuest()) {
users.save(u.mongoSerialise());
}
}
BasicDBObject serverObj = new BasicDBObject();
serverObj.put("_id","serverinfo"); // a constant id ensures only one entry is kept and updated, instead of a new entry created every save.
serverObj.put("time", gameUniverse.getTime());
serverObj.put("nextObjectId", gameUniverse.getNextObjectId());
server.save(serverObj);
LogManager.LOGGER.info(""+insertedWorlds+" worlds saved, "+unloaded_worlds+" unloaded");
LogManager.LOGGER.info("Done!");
} catch (Exception e) {
LogManager.LOGGER.severe("Problem happened during save function");
e.printStackTrace();
}
}
public ServerConfiguration getConfig() {
return config;
}
public PluginManager getPluginManager() {
return pluginManager;
}
public void setSocketServer(SocketServer socketServer) {
this.socketServer = socketServer;
}
public DayNightCycle getDayNightCycle() {
return dayNightCycle;
}
}

View File

@@ -1,26 +0,0 @@
package net.simon987.server;
import net.simon987.server.logging.LogManager;
import net.simon987.server.webserver.SocketServer;
import java.net.InetSocketAddress;
public class Main {
public static void main(String[] args) {
ServerConfiguration config = new ServerConfiguration("config.properties");
LogManager.initialize(config);
//Load
GameServer.INSTANCE.load();
SocketServer socketServer = new SocketServer(new InetSocketAddress(config.getString("webSocket_host"),
config.getInt("webSocket_port")), config);
GameServer.INSTANCE.setSocketServer(socketServer);
(new Thread(socketServer)).start();
(new Thread(GameServer.INSTANCE)).start();
}
}

View File

@@ -1,43 +0,0 @@
package net.simon987.server;
import net.simon987.server.logging.LogManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* Wrapper for Java Property
*/
public class ServerConfiguration {
/**
* Properties
*/
private Properties properties;
public ServerConfiguration(String file) {
try {
properties = new Properties();
InputStream is = new FileInputStream("config.properties");
properties.load(is);
} catch (IOException e) {
LogManager.LOGGER.severe("Problem loading server configuration: " + e.getMessage());
}
}
public int getInt(String key) {
return Integer.valueOf((String) properties.get(key));
}
public String getString(String key) {
return (String) properties.get(key);
}
}

View File

@@ -1,473 +0,0 @@
package net.simon987.server.assembly;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.assembly.exception.CancelledException;
import net.simon987.server.assembly.instruction.*;
import net.simon987.server.event.CpuInitialisationEvent;
import net.simon987.server.event.GameEvent;
import net.simon987.server.io.MongoSerialisable;
import net.simon987.server.logging.LogManager;
import net.simon987.server.user.User;
import java.util.HashMap;
/**
* CPU: Central Processing Unit. A CPU is capable of reading bytes from
* a Memory object and execute them. A CPU object holds registers objects &
* a Memory object.
*/
public class CPU implements MongoSerialisable {
/**
*
*/
private Status status;
/**
* Memory associated with the CPU, 64kb max
*/
private Memory memory;
/**
* set of instructions of this CPU
*/
private InstructionSet instructionSet;
/**
* set of registers of this CPU
*/
private RegisterSet registerSet;
/**
* Offset of the code section. The code starts to get
* executed at this address each tick. Defaults to org_offset@config.properties
*/
private int codeSectionOffset;
/**
* Instruction pointer, always points to the next instruction
*/
private int ip;
/**
* List of attached hardware, 'modules'
*/
private HashMap<Integer, CpuHardware> attachedHardware;
private ServerConfiguration config;
private int registerSetSize;
private static final char EXECUTION_COST_ADDR = 0x0050;
private static final char EXECUTED_INS_ADDR = 0x0051;
/**
* Creates a new CPU
*/
public CPU(ServerConfiguration config, User user) throws CancelledException {
this.config = config;
instructionSet = new DefaultInstructionSet();
registerSet = new DefaultRegisterSet();
attachedHardware = new HashMap<>();
codeSectionOffset = config.getInt("org_offset");
instructionSet.add(new JmpInstruction(this));
instructionSet.add(new JnzInstruction(this));
instructionSet.add(new JzInstruction(this));
instructionSet.add(new JgInstruction(this));
instructionSet.add(new JgeInstruction(this));
instructionSet.add(new JleInstruction(this));
instructionSet.add(new JlInstruction(this));
instructionSet.add(new PushInstruction(this));
instructionSet.add(new PopInstruction(this));
instructionSet.add(new CallInstruction(this));
instructionSet.add(new RetInstruction(this));
instructionSet.add(new MulInstruction(this));
instructionSet.add(new DivInstruction(this));
instructionSet.add(new JnsInstruction(this));
instructionSet.add(new JsInstruction(this));
instructionSet.add(new HwiInstruction(this));
instructionSet.add(new HwqInstruction(this));
instructionSet.add(new XchgInstruction(this));
instructionSet.add(new JcInstruction(this));
instructionSet.add(new JncInstruction(this));
instructionSet.add(new JnoInstruction(this));
instructionSet.add(new JoInstruction(this));
instructionSet.add(new PushfInstruction(this));
instructionSet.add(new PopfInstruction(this));
instructionSet.add(new JnaInstruction(this));
instructionSet.add(new JaInstruction(this));
status = new Status();
memory = new Memory(config.getInt("memory_size"));
GameEvent event = new CpuInitialisationEvent(this, user);
GameServer.INSTANCE.getEventDispatcher().dispatch(event);
if (event.isCancelled()) {
throw new CancelledException();
}
}
public void reset() {
status.clear();
ip = codeSectionOffset;
}
public int execute(int timeout) {
long startTime = System.currentTimeMillis();
int counter = 0;
status.clear();
registerSetSize = registerSet.size();
// status.breakFlag = true;
while (!status.isBreakFlag()) {
counter++;
if (counter % 10000 == 0) {
if (System.currentTimeMillis() > (startTime + timeout)) {
LogManager.LOGGER.fine("CPU Timeout " + this + " after " + counter + "instructions (" + timeout + "ms): " + (double) counter / ((double) timeout / 1000) / 1000000 + "MHz");
//Write execution cost and instruction count to memory
memory.set(EXECUTION_COST_ADDR, timeout);
memory.set(EXECUTED_INS_ADDR, Util.getHigherWord(counter));
memory.set(EXECUTED_INS_ADDR + 1, Util.getLowerWord(counter));
return timeout;
}
}
//fetch instruction
int machineCode = memory.get(ip);
/*
* Contents of machineCode should look like this:
* SSSS SDDD DDOO OOOO
* Where S is source, D is destination and O is the opCode
*/
Instruction instruction = instructionSet.get(machineCode & 0x03F); // 0000 0000 00XX XXXX
int source = (machineCode >> 11) & 0x001F; // XXXX X000 0000 0000
int destination = (machineCode >> 6) & 0x001F; // 0000 0XXX XX00 0000
executeInstruction(instruction, source, destination);
// LogManager.LOGGER.info(instruction.getMnemonic());
}
int elapsed = (int) (System.currentTimeMillis() - startTime);
LogManager.LOGGER.fine(counter + " instruction in " + elapsed + "ms : " + (double) counter / (elapsed / 1000) / 1000000 + "MHz");
//Write execution cost and instruction count to memory
memory.set(EXECUTION_COST_ADDR, elapsed);
memory.set(EXECUTED_INS_ADDR, Util.getHigherWord(counter));
memory.set(EXECUTED_INS_ADDR + 1, Util.getLowerWord(counter));
return elapsed;
}
public void executeInstruction(Instruction instruction, int source, int destination) {
//Execute the instruction
if (source == 0) {
//No operand (assuming that destination is also null)
ip++;
instruction.execute(status);
} else if (source == Operand.IMMEDIATE_VALUE) {
ip++;
int sourceValue = memory.get(ip);
if (destination == 0) {
//Single operand
ip++;
instruction.execute(sourceValue, status);
} else if (destination == Operand.IMMEDIATE_VALUE) {
//Destination is an immediate value too
//this shouldn't happen
LogManager.LOGGER.severe("Trying to execute an instruction with 2" +
"immediate values as operands"); //todo remove debug info
} else if (destination == Operand.IMMEDIATE_VALUE_MEM) {
//Destination is memory immediate
ip += 2;
instruction.execute(memory, memory.get(ip - 1), sourceValue, status);
} else if (destination <= registerSetSize) {
//Destination is a register
ip++;
instruction.execute(registerSet, destination, sourceValue, status);
} else if (destination <= registerSetSize * 2) {
//Destination is [reg]
ip++;
instruction.execute(memory, registerSet.get(destination - registerSetSize), sourceValue, status);
} else {
//Assuming that destination is [reg + x]
ip += 2;
instruction.execute(memory, registerSet.get(destination - registerSetSize - registerSetSize) + memory.get(ip - 1),
sourceValue, status);
}
} else if (source == Operand.IMMEDIATE_VALUE_MEM) {
//Source is [x]
ip++;
int sourceValue = memory.get(memory.get(ip));
if (destination == 0) {
//Single operand
ip++;
instruction.execute(memory, memory.get(ip - 1), status);
} else if (destination == Operand.IMMEDIATE_VALUE) {
//Destination is an immediate value
//this shouldn't happen
LogManager.LOGGER.severe("Trying to execute an instruction with an" +
"immediate values as dst operand"); //todo remove debug info
} else if (destination == Operand.IMMEDIATE_VALUE_MEM) {
//Destination is memory immediate too
ip += 2;
instruction.execute(memory, memory.get(ip - 1), sourceValue, status);
} else if (destination <= registerSetSize) {
//Destination is a register
ip++;
instruction.execute(registerSet, destination, sourceValue, status);
} else if (destination <= registerSetSize * 2) {
//Destination is [reg]
ip++;
instruction.execute(memory, registerSet.get(destination - registerSetSize), sourceValue, status);
} else {
//Assuming that destination is [reg + x]
ip += 2;
instruction.execute(memory, registerSet.get(destination - registerSetSize - registerSetSize) + memory.get(ip - 1), sourceValue, status);
}
} else if (source <= registerSetSize) {
//Source is a register
if (destination == 0) {
//Single operand
ip++;
instruction.execute(registerSet, source, status);
} else if (destination == Operand.IMMEDIATE_VALUE) {
//Destination is an immediate value
//this shouldn't happen
LogManager.LOGGER.severe("Trying to execute an instruction with an" +
"immediate values as dst operand"); //todo remove debug info
} else if (destination == Operand.IMMEDIATE_VALUE_MEM) {
//Destination is memory immediate
ip += 2;
instruction.execute(memory, memory.get(ip - 1), registerSet, source, status);
} else if (destination <= registerSetSize) {
//Destination is a register too
ip++;
instruction.execute(registerSet, destination, registerSet, source, status);
} else if (destination <= registerSetSize * 2) {
//Destination is [reg]
ip++;
instruction.execute(memory, registerSet.get(destination - registerSetSize), registerSet, source, status);
} else {
//Assuming that destination is [reg + x]
ip += 2;
instruction.execute(memory, registerSet.get(destination - registerSetSize - registerSetSize) + memory.get(ip - 1),
registerSet, source, status);
}
} else if (source <= registerSetSize * 2) {
//Source is [reg]
if (destination == 0) {
//Single operand
ip++;
instruction.execute(memory, registerSet.get(source - registerSetSize), status);
} else if (destination == Operand.IMMEDIATE_VALUE) {
//Destination is an immediate value
//this shouldn't happen
LogManager.LOGGER.severe("Trying to execute an instruction with an" +
"immediate values as dst operand"); //todo remove debug info
} else if (destination == Operand.IMMEDIATE_VALUE_MEM) {
//Destination is an memory immediate
ip++;
instruction.execute(memory, memory.get(ip++), memory, registerSet.get(source - registerSetSize), status);
} else if (destination <= registerSetSize) {
//Destination is a register
ip++;
instruction.execute(registerSet, destination, memory, registerSet.get(source - registerSetSize), status);
} else if (destination <= registerSetSize * 2) {
//Destination is [reg]
ip++;
instruction.execute(memory, registerSet.get(destination - registerSetSize), memory, registerSet.get(source - registerSetSize), status);
} else {
//Assuming that destination is [reg + x]
ip += 2;
instruction.execute(memory, registerSet.get(destination - registerSetSize - registerSetSize) + memory.get(ip - 1),
memory, registerSet.get(source - registerSetSize), status);
}
} else {
//Assuming that source is [reg + X]
ip++;
int sourceDisp = memory.get(ip);
if (destination == 0) {
//Single operand
ip += 1;
instruction.execute(memory, registerSet.get(source - registerSetSize - registerSetSize) + memory.get(ip - 1), status);
} else if (destination == Operand.IMMEDIATE_VALUE) {
//Destination is an immediate value
//this shouldn't happen
LogManager.LOGGER.severe("Trying to execute an instruction with an" +
"immediate values as dst operand"); //todo remove debug info
} else if (destination == Operand.IMMEDIATE_VALUE_MEM) {
//Destination is memory immediate
ip += 2;
instruction.execute(memory, memory.get(ip - 1), memory,
registerSet.get(source - registerSetSize - registerSetSize) + sourceDisp, status);
} else if (destination <= registerSetSize) {
//Destination is a register
ip++;
instruction.execute(registerSet, destination, memory,
registerSet.get(source - registerSetSize - registerSetSize) + sourceDisp, status);
} else if (destination <= registerSetSize * 2) {
//Destination is [reg]
ip++;
instruction.execute(memory, registerSet.get(destination - registerSetSize), memory,
registerSet.get(source - registerSetSize - registerSetSize) + sourceDisp, status);
} else {
//Assuming that destination is [reg + x]
ip += 2;
instruction.execute(memory, registerSet.get(destination - registerSetSize - registerSetSize) + memory.get(ip - 1),
memory, registerSet.get(source - registerSetSize - registerSetSize) + sourceDisp, status);
}
}
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("memory", memory.mongoSerialise());
dbObject.put("registerSet", registerSet.mongoSerialise());
dbObject.put("codeSegmentOffset", codeSectionOffset);
BasicDBList hardwareList = new BasicDBList();
for (Integer address : attachedHardware.keySet()) {
CpuHardware hardware = attachedHardware.get(address);
BasicDBObject serialisedHw = hardware.mongoSerialise();
serialisedHw.put("address", address);
hardwareList.add(serialisedHw);
}
dbObject.put("hardware", hardwareList);
return dbObject;
}
public static CPU deserialize(DBObject obj, User user) throws CancelledException {
CPU cpu = new CPU(GameServer.INSTANCE.getConfig(), user);
cpu.codeSectionOffset = (int) obj.get("codeSegmentOffset");
BasicDBList hardwareList = (BasicDBList) obj.get("hardware");
for (Object serialisedHw : hardwareList) {
CpuHardware hardware = CpuHardware.deserialize((DBObject) serialisedHw);
hardware.setCpu(cpu);
cpu.attachHardware(hardware, (int) ((BasicDBObject) serialisedHw).get("address"));
}
cpu.memory = Memory.deserialize((DBObject) obj.get("memory"));
cpu.registerSet = RegisterSet.deserialize((DBObject) obj.get("registerSet"));
return cpu;
}
public InstructionSet getInstructionSet() {
return instructionSet;
}
public RegisterSet getRegisterSet() {
return registerSet;
}
public Memory getMemory() {
return memory;
}
public Status getStatus() {
return status;
}
public int getIp() {
return ip;
}
public void setIp(char ip) {
this.ip = ip;
}
public void setCodeSectionOffset(int codeSectionOffset) {
this.codeSectionOffset = codeSectionOffset;
}
public void attachHardware(CpuHardware hardware, int address) {
attachedHardware.put(address, hardware);
}
public void detachHardware(int address) {
attachedHardware.remove(address);
}
public boolean hardwareInterrupt(int address) {
CpuHardware hardware = attachedHardware.get(address);
if (hardware != null) {
hardware.handleInterrupt(status);
return true;
} else {
return false;
}
}
public void hardwareQuery(int address) {
CpuHardware hardware = attachedHardware.get(address);
if (hardware != null) {
registerSet.getRegister("B").setValue(hardware.getId());
} else {
registerSet.getRegister("B").setValue(0);
}
}
@Override
public String toString() {
String str = "------------------------\n";
str += registerSet.toString();
str += status.toString();
str += "------------------------\n";
return str;
}
public CpuHardware getHardware(int address) {
return attachedHardware.get(address);
}
}

View File

@@ -1,45 +0,0 @@
package net.simon987.server.assembly;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.io.CpuHardwareDeserializer;
import net.simon987.server.io.MongoSerialisable;
import net.simon987.server.plugin.ServerPlugin;
public abstract class CpuHardware implements MongoSerialisable {
CPU cpu;
/**
* Handle an HWI instruction
*/
public abstract void handleInterrupt(Status status);
public CPU getCpu() {
return cpu;
}
public void setCpu(CPU cpu) {
this.cpu = cpu;
}
public abstract char getId();
public static CpuHardware deserialize(DBObject obj) {
for (ServerPlugin plugin : GameServer.INSTANCE.getPluginManager().getPlugins()) {
if (plugin instanceof CpuHardwareDeserializer) {
CpuHardware hw = ((CpuHardwareDeserializer) plugin).deserializeHardware(obj);
if (hw != null) {
return hw;
}
}
}
return null;
}
}

View File

@@ -1,22 +0,0 @@
package net.simon987.server.assembly;
/**
* RegisterSet with default values
*/
class DefaultRegisterSet extends RegisterSet {
DefaultRegisterSet() {
super();
addRegister(1, new Register("A"));
addRegister(2, new Register("B"));
addRegister(3, new Register("C"));
addRegister(4, new Register("D"));
addRegister(5, new Register("X"));
addRegister(6, new Register("Y"));
addRegister(7, new Register("SP"));
addRegister(8, new Register("BP"));
}
}

View File

@@ -1,212 +0,0 @@
package net.simon987.server.assembly;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.io.MongoSerialisable;
import net.simon987.server.logging.LogManager;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
/**
* A set of registers for a CPU
*/
public class RegisterSet implements Target, MongoSerialisable {
/**
* List of registers
*/
private HashMap<Integer, Register> registers = new HashMap<>(8);
/**
* Create an empty Register set
*/
public RegisterSet() {
}
/**
* Get the index of a Register by its name
* This method assumes that the
*
* @param name Name of the register
* @return index of the register, -1 if not found
*/
int getIndex(String name) {
name = name.toUpperCase();
for (Integer i : registers.keySet()) {
if (registers.get(i).getName().equals(name)) {
return i;
}
}
return -1;
}
/**
* Get a register by its index
*
* @param index index of the register
*/
public Register getRegister(int index) {
return registers.get(index);
}
/**
* Get a register by its name (e.g. "AX")
*
* @param name Name of the register, case insensitive
*/
public Register getRegister(String name) {
name = name.toUpperCase();
for (Register r : registers.values()) {
if (r.getName().equals(name)) {
return r;
}
}
return null;
}
/**
* Get the value of a register
*
* @param address Address of the value. Can refer to a memory address or the index
* of a register
* @return 16-bit value of a register
*/
@Override
public int get(int address) {
Register register = registers.get(address);
if (register != null) {
return register.getValue();
} else {
return 0;
}
}
/**
* Set the value of a register
*
* @param address index of the value to change
* @param value value to set
*/
@Override
public void set(int address, int value) {
Register register = registers.get(address);
if (register != null) {
register.setValue(value);
} else {
LogManager.LOGGER.info("DEBUG: trying to set unknown reg index : " + address);
}
}
public void put(int index, Register register) {
registers.put(index, register);
}
public void clear() {
for (Register register : registers.values()) {
register.setValue(0);
}
}
/**
* Add a register to the register set
* <p>
* the register set will break if the indexes of the registers
* are not consecutive, starting at address 1.
*
* @param index Index of the register
* @param reg Register to add
*/
void addRegister(int index, Register reg) {
registers.put(index, reg);
}
int size() {
return registers.size();
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBList registers = new BasicDBList();
for (Integer index : this.registers.keySet()) {
JSONObject register = new JSONObject();
register.put("index", index);
register.put("name", getRegister(index).getName());
register.put("value", (int) getRegister(index).getValue());
registers.add(register);
}
BasicDBObject obj = new BasicDBObject();
obj.put("registers", registers);
return obj;
}
public static RegisterSet deserialize(DBObject obj) {
RegisterSet registerSet = new RegisterSet();
BasicDBList registers = (BasicDBList) obj.get("registers");
for (Object sRegister : registers) {
Register register = new Register((String) ((DBObject) sRegister).get("name"));
register.setValue((int) ((DBObject) sRegister).get("value"));
registerSet.registers.put((int) ((DBObject) sRegister).get("index"), register);
}
return registerSet;
}
public static RegisterSet deserialize(JSONObject json) {
RegisterSet registerSet = new RegisterSet();
JSONArray registers = (JSONArray) json.get("registers");
for (JSONObject jsonRegister : (ArrayList<JSONObject>) registers) {
Register register = new Register((String) jsonRegister.get("name"));
register.setValue((int) (long) jsonRegister.get("value"));
registerSet.registers.put((int) (long) jsonRegister.get("index"), register);
}
return registerSet;
}
@Override
public String toString() {
String str = "";
for (Integer index : registers.keySet()) {
str += index + " " + registers.get(index).getName() + "=" + Util.toHex(registers.get(index).getValue()) + "\n";
}
return str;
}
}

View File

@@ -1,40 +0,0 @@
package net.simon987.server.assembly.instruction;
import net.simon987.server.assembly.CPU;
import net.simon987.server.assembly.Instruction;
import net.simon987.server.assembly.Status;
import net.simon987.server.assembly.Target;
/**
* Send hardware interupt
* Used to interact with the World using hardware
* </p>
*/
public class HwiInstruction extends Instruction {
public static final int OPCODE = 9;
private CPU cpu;
public HwiInstruction(CPU cpu) {
super("hwi", OPCODE);
this.cpu = cpu;
}
@Override
public Status execute(Target src, int srcIndex, Status status) {
status.setErrorFlag(cpu.hardwareInterrupt(src.get(srcIndex)));
return status;
}
@Override
public Status execute(int src, Status status) {
status.setErrorFlag(cpu.hardwareInterrupt(src));
return status;
}
}

View File

@@ -1,14 +0,0 @@
package net.simon987.server.assembly.instruction;
import net.simon987.server.assembly.Instruction;
/**
* NOP (No operation instruction).
* Does nothing
*/
public class NopInstruction extends Instruction {
public NopInstruction() {
super("nop", 63);
}
}

View File

@@ -1,13 +0,0 @@
package net.simon987.server.assembly.instruction;
import net.simon987.server.assembly.Instruction;
/**
* Alias of SHL instruction
*/
public class SalInstruction extends Instruction {
public SalInstruction() {
super("sal", ShlInstruction.OPCODE);
}
}

View File

@@ -1,18 +0,0 @@
package net.simon987.server.event;
import net.simon987.server.assembly.CPU;
import net.simon987.server.user.User;
public class CpuInitialisationEvent extends GameEvent {
private User user;
public CpuInitialisationEvent(CPU cpu, User user) {
setSource(cpu);
this.user = user;
}
public User getUser() {
return user;
}
}

View File

@@ -1,43 +0,0 @@
package net.simon987.server.event;
import net.simon987.server.plugin.PluginManager;
import net.simon987.server.plugin.ServerPlugin;
import java.util.ArrayList;
public class GameEventDispatcher {
private PluginManager pluginManager;
private ArrayList<GameEventListener> listeners;
public GameEventDispatcher(PluginManager pluginManager) {
this.pluginManager = pluginManager;
listeners = new ArrayList<>(5);
}
public void dispatch(GameEvent event) {
//Dispatch to 'local' listeners
for (GameEventListener listener : listeners) {
if (event.getClass().equals(listener.getListenedEventType())) {
listener.handle(event);
}
}
//Dispatch to plugins
for (ServerPlugin plugin : pluginManager.getPlugins()) {
for (GameEventListener listener : plugin.getListeners()) {
if (event.getClass().equals(listener.getListenedEventType())) {
listener.handle(event);
}
}
}
}
public ArrayList<GameEventListener> getListeners() {
return listeners;
}
}

View File

@@ -1,20 +0,0 @@
package net.simon987.server.event;
/**
* Event dispatched by a GameObject who has needed callbacks on death
*/
public class ObjectDeathEvent extends GameEvent {
/**
* The GameObject type ID of object that init this event
*/
private long sourceObjectId;
public ObjectDeathEvent(Object source, int sourceObjectId) {
setSource(source);
this.sourceObjectId = sourceObjectId;
}
public long getSourceObjectId() {
return sourceObjectId;
}
}

View File

@@ -1,11 +0,0 @@
package net.simon987.server.game;
public enum Action {
IDLE,
DIGGING,
WALKING,
WITHDRAWING,
DEPOSITING,
LISTENING
}

View File

@@ -1,35 +0,0 @@
package net.simon987.server.game;
import net.simon987.server.assembly.Memory;
import net.simon987.server.user.User;
import java.util.ArrayList;
public interface ControllableUnit {
long getObjectId();
void setKeyboardBuffer(ArrayList<Integer> kbBuffer);
void setParent(User user);
ArrayList<Integer> getKeyboardBuffer();
Memory getFloppyData();
boolean spendEnergy(int energy);
int getEnergy();
int getX();
int getY();
void setAction(Action listening);
World getWorld();
ArrayList<char[]> getConsoleMessagesBuffer();
int getConsoleMode();
}

View File

@@ -1,262 +0,0 @@
package net.simon987.server.game;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.io.GameObjectDeserializer;
import net.simon987.server.io.JSONSerialisable;
import net.simon987.server.io.MongoSerialisable;
import net.simon987.server.plugin.ServerPlugin;
import org.json.simple.JSONObject;
import java.awt.*;
/**
* An INSTANCE of an object (e.g. a Tree, a character ...) inside the
* game universe
*/
public abstract class GameObject implements JSONSerialisable, MongoSerialisable {
private boolean dead;
/**
* Object's unique identifier
*/
private long objectId;
/**
* X coordinate of the object in its World
*/
private int x;
/**
* Y coordinate of the object in its World
*/
private int y;
/**
* Direction of the object
*/
private Direction direction = Direction.NORTH;
/**
* Current World of the object
*/
private World world;
//--------
/**
* Increment the location of the game object by 1 tile
* Collision checks happen here
*/
public boolean incrementLocation() {
int newX = 0, newY = 0;
if (direction == Direction.NORTH) {
newX = x;
newY = (y - 1);
} else if (direction == Direction.EAST) {
newX = (x + 1);
newY = y;
} else if (direction == Direction.SOUTH) {
newX = x;
newY = (y + 1);
} else if (direction == Direction.WEST) {
newX = (x - 1);
newY = y;
}
//Check if out of World bounds / collision
if (newX < 0) {
//Move object to adjacent World (left)
World leftWorld;
if (world.getX() == 0) {
//Warp around
leftWorld = GameServer.INSTANCE.getGameUniverse().getWorld(
GameServer.INSTANCE.getGameUniverse().getMaxWidth(), world.getY(), true);
} else {
leftWorld = GameServer.INSTANCE.getGameUniverse().getWorld(world.getX() - 1, world.getY(), true);
}
if (leftWorld != null) {
world.removeObject(this);
world.decUpdatable();
leftWorld.addObject(this);
leftWorld.incUpdatable();
setWorld(leftWorld);
x = World.WORLD_SIZE - 1;
}
} else if (newX >= World.WORLD_SIZE) {
//Move object to adjacent World (right)
World rightWorld;
if (world.getX() == GameServer.INSTANCE.getGameUniverse().getMaxWidth()) {
//Warp around
rightWorld = GameServer.INSTANCE.getGameUniverse().getWorld(0, world.getY(), true);
} else {
rightWorld = GameServer.INSTANCE.getGameUniverse().getWorld(world.getX() + 1, world.getY(), true);
}
if (rightWorld != null) {
world.removeObject(this);
world.decUpdatable();
rightWorld.addObject(this);
rightWorld.incUpdatable();
setWorld(rightWorld);
x = 0;
}
} else if (newY < 0) {
//Move object to adjacent World (up)
World upWorld;
if (world.getY() == 0) {
//Warp around
upWorld = GameServer.INSTANCE.getGameUniverse().getWorld(world.getX(),
GameServer.INSTANCE.getGameUniverse().getMaxWidth(), true);
} else {
upWorld = GameServer.INSTANCE.getGameUniverse().getWorld(world.getX(), world.getY() - 1, true);
}
if (upWorld != null) {
world.removeObject(this);
world.decUpdatable();
upWorld.addObject(this);
upWorld.incUpdatable();
setWorld(upWorld);
y = World.WORLD_SIZE - 1;
}
} else if (newY >= World.WORLD_SIZE) {
//Move object to adjacent World (down)
World downWorld;
if (world.getY() == GameServer.INSTANCE.getGameUniverse().getMaxWidth()) {
//Warp around
downWorld = GameServer.INSTANCE.getGameUniverse().getWorld(world.getX(), 0, true);
} else {
downWorld = GameServer.INSTANCE.getGameUniverse().getWorld(world.getX(), world.getY() + 1, true);
}
if (downWorld != null) {
world.removeObject(this);
world.decUpdatable();
downWorld.addObject(this);
downWorld.incUpdatable();
setWorld(downWorld);
y = 0;
}
}
//Check collision
else if (!world.isTileBlocked(newX, newY)) {
//Tile is passable
x = newX;
y = newY;
} else {
return false;
}
return true;
}
public abstract char getMapInfo();
public Point getFrontTile() {
if (direction == Direction.NORTH) {
return new Point(x, y - 1);
} else if (direction == Direction.EAST) {
return new Point(x + 1, y);
} else if (direction == Direction.SOUTH) {
return new Point(x, y + 1);
} else {
return new Point(x - 1, y);
}
}
public long getObjectId() {
return objectId;
}
public void setObjectId(long objectId) {
this.objectId = objectId;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public Direction getDirection() {
return direction;
}
public void setDirection(Direction direction) {
this.direction = direction;
}
public World getWorld() {
return world;
}
public void setWorld(World world) {
this.world = world;
}
@Override
public JSONObject serialise() {
return new JSONObject();
}
public static GameObject deserialize(DBObject obj) {
//
for (ServerPlugin plugin : GameServer.INSTANCE.getPluginManager().getPlugins()) {
if (plugin instanceof GameObjectDeserializer) {
GameObject object = ((GameObjectDeserializer) plugin).deserializeObject(obj);
if (object != null) {
return object;
}
}
}
return null;
}
public boolean isAt(int x, int y) {
return this.x == x && this.y == y;
}
public boolean isDead() {
return dead;
}
public void setDead(boolean dead) {
this.dead = dead;
}
/**
* Called before this GameObject is removed from the world - defaults to doing nothing
*/
public void onDeadCallback() { }
}

View File

@@ -1,25 +0,0 @@
package net.simon987.server.game;
public interface InventoryHolder {
/**
* Place an item into the inventory
*
* @param item item id (see MarConstants.ITEM_*)
*/
boolean placeItem(int item);
/**
* Take an item from the inventory
*
* @param item Desired item id (see MarConstants.ITEM_*)
*/
void takeItem(int item);
/**
* @param item item to take
* @return true if the InventoryHolder can provide this item
*/
boolean canTakeItem(int item);
}

View File

@@ -1,7 +0,0 @@
package net.simon987.server.game;
public interface Programmable {
boolean sendMessage(char[] message);
}

View File

@@ -1,222 +0,0 @@
package net.simon987.server.game;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.io.JSONSerialisable;
import net.simon987.server.io.MongoSerialisable;
import org.json.simple.JSONObject;
import java.awt.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Random;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterOutputStream;
/**
* A 2D map of Tile objects of size width*height
*/
public class TileMap implements JSONSerialisable, MongoSerialisable {
public static final int PLAIN_TILE = 0;
public static final int WALL_TILE = 1;
public static final int IRON_TILE = 2;
public static final int COPPER_TILE = 3;
public static final int ITEM_IRON = 3;
public static final int ITEM_COPPER = 4;
/**
* The map of tile
*/
private int[][] tiles;
/**
* width, in tiles
*/
private int width;
/**
* Height, in tiles
*/
private int height;
/**
* Create a blank (All 0s) map
*/
public TileMap(int width, int height) {
this.width = width;
this.height = height;
tiles = new int[width][height];
}
public TileMap(int[][] tiles) {
this.width = World.WORLD_SIZE;
this.height = World.WORLD_SIZE;
this.tiles = tiles;
}
/**
* Change the tile at a specified position
* Sets the modified flag
*
* @param tileId id of the new Tile
* @param x X coordinate of the tile to set
* @param y Y coordinate of the tile to set
*/
public void setTileAt(int tileId, int x, int y) {
try {
tiles[x][y] = tileId;
} catch (ArrayIndexOutOfBoundsException e) {
//Shouldn't happen
e.printStackTrace();
}
}
/**
* Get the tile at a specific position
*
* @param x X coordinate of the tile to get
* @param y Y coordinate of the tile to get
* @return the tile at the specified position, -1 if out of bounds
*/
public int getTileAt(int x, int y) {
try {
return tiles[x][y];
} catch (ArrayIndexOutOfBoundsException e) {
return -1;
}
}
public int[][] getTiles() {
return tiles;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
@Override
public JSONObject serialise() {
JSONObject json = new JSONObject();
byte[] terrain = new byte[width * width];
for (int x = 0; x < World.WORLD_SIZE; x++) {
for (int y = 0; y < World.WORLD_SIZE; y++) {
terrain[x * width + y] = (byte) tiles[x][y];
}
}
try {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Deflater compressor = new Deflater(Deflater.BEST_COMPRESSION, true);
DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(stream, compressor);
deflaterOutputStream.write(terrain);
deflaterOutputStream.close();
byte[] compressedBytes = stream.toByteArray();
json.put("z", new String(Base64.getEncoder().encode(compressedBytes)));
} catch (IOException e) {
e.printStackTrace();
}
return json;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("tiles", tiles);
return dbObject;
}
public static TileMap deserialize(DBObject object) {
BasicDBList terrain = (BasicDBList) object.get("tiles");
int[][] tiles = new int[World.WORLD_SIZE][World.WORLD_SIZE];
for (int x = 0; x < World.WORLD_SIZE; x++) {
for (int y = 0; y < World.WORLD_SIZE; y++) {
tiles[x][y] = (int) ((BasicDBList) terrain.get(x)).get(y);
}
}
return new TileMap(tiles);
}
public static TileMap deserialize(JSONObject object) {
TileMap tileMap = new TileMap(World.WORLD_SIZE, World.WORLD_SIZE);
byte[] compressedBytes = Base64.getDecoder().decode((String) object.get("z"));
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Inflater decompressor = new Inflater(true);
InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(baos, decompressor);
inflaterOutputStream.write(compressedBytes);
inflaterOutputStream.close();
byte[] terrain = baos.toByteArray();
for (int x = 0; x < World.WORLD_SIZE; x++) {
for (int y = 0; y < World.WORLD_SIZE; y++) {
tileMap.tiles[x][y] = terrain[x * World.WORLD_SIZE + y];
}
}
return tileMap;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public Point getRandomPlainTile() {
Random random = new Random();
int counter = 0;
while (true) {
counter++;
//Prevent infinite loop
if (counter >= 2500) {
return null;
}
int rx = random.nextInt(World.WORLD_SIZE);
int ry = random.nextInt(World.WORLD_SIZE);
if (tiles[rx][ry] == TileMap.PLAIN_TILE) {
return new Point(rx, ry);
}
}
}
}

View File

@@ -1,10 +0,0 @@
package net.simon987.server.io;
import com.mongodb.DBObject;
import net.simon987.server.assembly.CpuHardware;
public interface CpuHardwareDeserializer {
CpuHardware deserializeHardware(DBObject hwJson);
}

View File

@@ -1,45 +0,0 @@
package net.simon987.server.io;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.logging.LogManager;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* Manages the database: this class manages the interactions with the database
*/
public abstract class DatabaseManager {
/**
* SQL connection url
*/
private String url;
/**
* SQL username
*/
private String username;
/**
* SQL password
*/
private String password;
public DatabaseManager(ServerConfiguration config) {
this.url = config.getString("mysql_url");
this.username = config.getString("mysql_user");
this.password = config.getString("mysql_pass");
}
protected Connection getConnection() {
try {
return DriverManager.getConnection(url, this.username, password);
} catch (SQLException e) {
LogManager.LOGGER.severe("Couldn't connect to MySQL server state:" + e.getSQLState() + " error:" + e.getErrorCode());
return null;
}
}
}

View File

@@ -1,22 +0,0 @@
package net.simon987.server.io;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileUtils {
private static final String DATE_FORMAT = "yyyyMMddHHmmss";
/**
* Creates a new stamp containing the current date and time
*
* @return date and time stamp
*/
private static String getDateTimeStamp() {
Date millisToDate = new Date(System.currentTimeMillis());
SimpleDateFormat f = new SimpleDateFormat(DATE_FORMAT);
return f.format(millisToDate);
}
}

View File

@@ -1,10 +0,0 @@
package net.simon987.server.io;
import com.mongodb.DBObject;
import net.simon987.server.game.GameObject;
public interface GameObjectDeserializer {
GameObject deserializeObject(DBObject object);
}

View File

@@ -1,9 +0,0 @@
package net.simon987.server.io;
import org.json.simple.JSONObject;
public interface JSONSerialisable {
JSONObject serialise();
}

View File

@@ -1,9 +0,0 @@
package net.simon987.server.io;
import com.mongodb.BasicDBObject;
public interface MongoSerialisable {
BasicDBObject mongoSerialise();
}

View File

@@ -1,72 +0,0 @@
package net.simon987.server.plugin;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.logging.LogManager;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Properties;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class PluginManager {
private ArrayList<ServerPlugin> plugins;
public PluginManager() {
this.plugins = new ArrayList<>(10);
}
public void load(File pluginFile, ServerConfiguration config) {
LogManager.LOGGER.info("Loading plugin file " + pluginFile.getName());
try {
//Get the plugin config file from the archive
ZipFile zipFile = new ZipFile(pluginFile);
ZipEntry configEntry = zipFile.getEntry("plugin.properties");
if (configEntry != null) {
InputStream stream = zipFile.getInputStream(configEntry);
Properties pluginConfig = new Properties();
pluginConfig.load(stream);
//Load the plugin
ClassLoader loader = URLClassLoader.newInstance(new URL[]{pluginFile.toURI().toURL()});
Class<?> aClass = Class.forName(pluginConfig.getProperty("classpath"), true, loader);
Class<? extends ServerPlugin> pluginClass = aClass.asSubclass(ServerPlugin.class);
Constructor<? extends ServerPlugin> constructor = pluginClass.getConstructor();
ServerPlugin plugin = constructor.newInstance();
plugin.setName(pluginConfig.getProperty("name"));
plugin.setVersion(pluginConfig.getProperty("version"));
LogManager.LOGGER.info("Loaded " + plugin.name + " V" + plugin.version);
//Add it to the list
plugins.add(plugin);
//Init the plugin
plugin.init(config);
} else {
LogManager.LOGGER.severe("Couldn't find plugin.properties in " + pluginFile.getName());
}
zipFile.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public ArrayList<ServerPlugin> getPlugins() {
return plugins;
}
}

View File

@@ -1,62 +0,0 @@
package net.simon987.server.plugin;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.io.JSONSerialisable;
import org.json.simple.JSONObject;
import java.util.ArrayList;
public abstract class ServerPlugin implements JSONSerialisable {
/**
* Name of the plugin
*/
protected String name;
/**
* Version of the plugin
*/
protected String version;
/**
* List of event listeners
*/
protected ArrayList<GameEventListener> listeners = new ArrayList<>(5);
/**
* Called when the plugin is loaded
*/
public abstract void init(ServerConfiguration config);
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public ArrayList<GameEventListener> getListeners() {
return listeners;
}
@Override
public JSONObject serialise() {
JSONObject json = new JSONObject();
json.put("name", name);
json.put("version", version);
return json;
}
}

View File

@@ -1,110 +0,0 @@
package net.simon987.server.user;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CPU;
import net.simon987.server.assembly.exception.CancelledException;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.UserCreationEvent;
import net.simon987.server.game.ControllableUnit;
import net.simon987.server.io.MongoSerialisable;
/**
* Represents a User (or player) of the game
*/
public class User implements MongoSerialisable {
private String username;
private String userCode;
private CPU cpu;
private ControllableUnit controlledUnit;
private boolean guest;
public User() throws CancelledException {
GameEvent event = new UserCreationEvent(this);
GameServer.INSTANCE.getEventDispatcher().dispatch(event);
if (event.isCancelled()) {
throw new CancelledException();
}
}
public User(ControllableUnit unit) {
this.controlledUnit = unit;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("_id", username); // a constant id ensures only one entry per user is kept and updated, instead of a new entry created every save for every user.
dbObject.put("username", username);
dbObject.put("code", userCode);
dbObject.put("controlledUnit", controlledUnit.getObjectId());
dbObject.put("cpu", cpu.mongoSerialise());
return dbObject;
}
public static User deserialize(DBObject obj) throws CancelledException {
User user = new User((ControllableUnit) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("controlledUnit")));
user.username = (String) obj.get("username");
user.userCode = (String) obj.get("code");
user.getControlledUnit().setParent(user);
user.cpu = CPU.deserialize((DBObject) obj.get("cpu"), user);
return user;
}
//----
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
public CPU getCpu() {
return cpu;
}
public void setCpu(CPU cpu) {
this.cpu = cpu;
}
public String getUsername() {
return username;
}
public ControllableUnit getControlledUnit() {
return controlledUnit;
}
public void setControlledUnit(ControllableUnit controlledUnit) {
this.controlledUnit = controlledUnit;
}
public void setUsername(String username) {
this.username = username;
}
public boolean isGuest() {
return guest;
}
public void setGuest(boolean guest) {
this.guest = guest;
}
}

View File

@@ -1,40 +0,0 @@
package net.simon987.server.webserver;
import net.simon987.server.GameServer;
import net.simon987.server.logging.LogManager;
import org.json.simple.JSONObject;
public class FloppyHandler implements MessageHandler {
SocketServerDatabase db = new SocketServerDatabase(GameServer.INSTANCE.getConfig());
@Override
public void handle(OnlineUser user, JSONObject json) {
if (json.get("t").equals("floppyDown")) {
LogManager.LOGGER.fine("(WS) Floppy download request from " + user.getUser().getUsername());
if (user.isGuest()) {
return;
}
if (user.getUser().getControlledUnit().getFloppyData() != null) {
byte[] bytes = user.getUser().getControlledUnit().getFloppyData().getBytes();
user.getWebSocket().send(bytes);
}
} else if (json.get("t").equals("floppyUp")) {
LogManager.LOGGER.fine("(WS) Floppy upload request from " + user.getUser().getUsername());
//Check newly uploaded file on the database
byte[] bytes = db.getFloppy(user.getUser().getUsername());
if (bytes != null) {
user.getUser().getControlledUnit().getFloppyData().setBytes(bytes);
}
}
}
}

View File

@@ -1,10 +0,0 @@
package net.simon987.server.webserver;
import org.java_websocket.exceptions.WebsocketNotConnectedException;
import org.json.simple.JSONObject;
public interface MessageHandler {
void handle(OnlineUser user, JSONObject json) throws WebsocketNotConnectedException;
}

View File

@@ -1,49 +0,0 @@
package net.simon987.server.webserver;
import org.java_websocket.WebSocket;
import java.util.ArrayList;
public class OnlineUserManager {
/**
* List of online users.
*/
private ArrayList<OnlineUser> onlineUsers = new ArrayList<>(10);
public OnlineUser getUser(WebSocket socket) {
ArrayList<OnlineUser> _onlineUsers = new ArrayList<>(onlineUsers);
for (OnlineUser user : _onlineUsers) {
if (user.getWebSocket().equals(socket)) {
return user;
}
}
return null;
}
/**
* Add an user to the list
*
* @param user user to add
*/
public void add(OnlineUser user) {
onlineUsers.add(user);
}
/**
* Remove an user to the list
*
* @param user user to remove
*/
public void remove(OnlineUser user) {
onlineUsers.remove(user);
}
public ArrayList<OnlineUser> getOnlineUsers() {
return onlineUsers;
}
}

View File

@@ -1,319 +0,0 @@
package net.simon987.server.webserver;
import net.simon987.server.GameServer;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.game.ControllableUnit;
import net.simon987.server.logging.LogManager;
import net.simon987.server.user.User;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.DefaultSSLWebSocketServerFactory;
import org.java_websocket.server.WebSocketServer;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.xml.bind.DatatypeConverter;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.security.KeyFactory;
import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
public class SocketServer extends WebSocketServer {
private OnlineUserManager userManager = new OnlineUserManager();
private SocketServerDatabase database;
private MessageEventDispatcher messageEventDispatcher = new MessageEventDispatcher();
public SocketServer(InetSocketAddress address, ServerConfiguration config) {
super(address);
if (config.getInt("use_secure_webSocket") != 0) {
SSLContext context = getContext(config.getString("cert_path"));
if (context != null) {
setWebSocketFactory(new DefaultSSLWebSocketServerFactory(context));
LogManager.LOGGER.info("(WS) Enabled secure webSocket");
} else {
LogManager.LOGGER.severe("(WS) Failed to create SSL context");
}
}
setConnectionLostTimeout(120);
setReuseAddr(true); //To avoid BindException
database = new SocketServerDatabase(config);
messageEventDispatcher.addHandler(new UserInfoRequestHandler());
messageEventDispatcher.addHandler(new TerrainRequestHandler());
messageEventDispatcher.addHandler(new ObjectsRequestHandler());
messageEventDispatcher.addHandler(new CodeUploadHandler());
messageEventDispatcher.addHandler(new CodeRequestHandler());
messageEventDispatcher.addHandler(new KeypressHandler());
messageEventDispatcher.addHandler(new FloppyHandler());
}
@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
LogManager.LOGGER.info("(WS) New Websocket connection " + conn.getRemoteSocketAddress());
userManager.add(new OnlineUser(conn));
}
@Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
LogManager.LOGGER.info("(WS) Closed " + conn.getRemoteSocketAddress() + " with exit code " + code + " additional info: " + reason);
userManager.remove(userManager.getUser(conn));
}
@Override
public void onMessage(WebSocket conn, String message) {
OnlineUser onlineUser = userManager.getUser(conn);
if (onlineUser != null) {
if (onlineUser.isAuthenticated()) {
messageEventDispatcher.dispatch(onlineUser, message);
} else {
LogManager.LOGGER.info("(WS) Received message from unauthenticated user " + conn.getRemoteSocketAddress());
//We expect a 128 characters long token
if (message.length() == 128) {
String username = database.validateAuthToken(message);
if (username != null) {
User user = GameServer.INSTANCE.getGameUniverse().getOrCreateUser(username, true);
LogManager.LOGGER.info("(WS) User was successfully authenticated: " + user.getUsername());
onlineUser.setUser(user);
onlineUser.setAuthenticated(true);
conn.send("{\"t\":\"auth\", \"m\":\"ok\"}");
} else {
User user = GameServer.INSTANCE.getGameUniverse().getOrCreateUser(GameServer.INSTANCE.getGameUniverse().getGuestUsername(), false);
onlineUser.setUser(user);
onlineUser.setAuthenticated(true);
onlineUser.setGuest(true);
LogManager.LOGGER.info("(WS) Created guest user " +
onlineUser.getUser().getUsername() + conn.getRemoteSocketAddress());
conn.send("{\"t\":\"auth\", \"m\":\"ok\"}");
}
}
}
} else {
LogManager.LOGGER.severe("(WS) FIXME: SocketServer:onMessage");
}
}
@Override
public void onMessage(WebSocket conn, ByteBuffer message) {
//System.out.println("received ByteBuffer from " + conn.getRemoteSocketAddress());
}
@Override
public void onError(WebSocket conn, Exception ex) {
if (ex instanceof BindException) {
LogManager.LOGGER.severe("Address already in use");
System.exit(-1);
} else {
LogManager.LOGGER.severe("an error occurred on connection " + conn + ": " + ex);
userManager.remove(userManager.getUser(conn));
ex.printStackTrace();
}
}
@Override
public void onStart() {
LogManager.LOGGER.info("(WS) Server started successfully");
}
/**
* Called every tick
*/
public void tick() {
JSONObject json = new JSONObject();
json.put("t", "tick");
LogManager.LOGGER.info("Notified " + userManager.getOnlineUsers().size() + " users");
ArrayList<OnlineUser> onlineUsers = new ArrayList<>(userManager.getOnlineUsers()); //Avoid ConcurrentModificationException
for (OnlineUser user : onlineUsers) {
if (user.getWebSocket().isOpen()) {
if (user.isGuest()) {
json.remove("c");
user.getWebSocket().send(json.toJSONString());
} else {
try {
ControllableUnit unit = user.getUser().getControlledUnit();
//Send keyboard updated buffer
ArrayList<Integer> kbBuffer = unit.getKeyboardBuffer();
JSONArray keys = new JSONArray();
keys.addAll(kbBuffer);
json.put("keys", keys);
//Send console buffer
if (unit.getConsoleMessagesBuffer().size() > 0) {
JSONArray buff = new JSONArray();
for (char[] message : unit.getConsoleMessagesBuffer()) {
buff.add(new String(message));
}
json.put("c", buff);
} else {
json.remove("c");
}
json.put("cm", unit.getConsoleMode());
//Send tick message
user.getWebSocket().send(json.toJSONString());
} catch (NullPointerException e) {
//User is online but not completely initialised
}
}
}
}
}
public OnlineUserManager getUserManager() {
return userManager;
}
/**
* See https://github.com/TooTallNate/Java-WebSocket/blob/master/src/main/example/SSLServerLetsEncryptExample.java
*/
/*
* * Copyright (c) 2010-2017 Nathan Rajlich
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*/
private static SSLContext getContext(String pathTo) {
SSLContext context;
String password = "MAR";
try {
context = SSLContext.getInstance("TLS");
byte[] certBytes = parseDERFromPEM(getBytes(new File(pathTo + File.separator + "cert.pem")),
"-----BEGIN CERTIFICATE-----", "-----END CERTIFICATE-----");
byte[] keyBytes = parseDERFromPEM(getBytes(new File(pathTo + File.separator + "privkey.pem")),
"-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----");
X509Certificate cert = generateCertificateFromDER(certBytes);
RSAPrivateKey key = generatePrivateKeyFromDER(keyBytes);
KeyStore keystore = KeyStore.getInstance("JKS");
keystore.load(null);
keystore.setCertificateEntry("cert-alias", cert);
keystore.setKeyEntry("key-alias", key, password.toCharArray(), new Certificate[]{cert});
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, password.toCharArray());
KeyManager[] km = kmf.getKeyManagers();
context.init(km, null, null);
} catch (Exception e) {
context = null;
}
return context;
}
private static byte[] parseDERFromPEM(byte[] pem, String beginDelimiter, String endDelimiter) {
String data = new String(pem);
String[] tokens = data.split(beginDelimiter);
tokens = tokens[1].split(endDelimiter);
return DatatypeConverter.parseBase64Binary(tokens[0]);
}
private static RSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException {
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory factory = KeyFactory.getInstance("RSA");
return (RSAPrivateKey) factory.generatePrivate(spec);
}
private static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(certBytes));
}
private static byte[] getBytes(File file) {
byte[] bytesArray = new byte[(int) file.length()];
FileInputStream fis;
try {
fis = new FileInputStream(file);
fis.read(bytesArray); //read file into bytes[]
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
return bytesArray;
}
}

View File

@@ -1,82 +0,0 @@
package net.simon987.server.webserver;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.io.DatabaseManager;
import net.simon987.server.logging.LogManager;
import java.sql.*;
class SocketServerDatabase extends DatabaseManager {
public SocketServerDatabase(ServerConfiguration config) {
super(config);
}
String validateAuthToken(String token) {
Connection connection = null;
try {
connection = getConnection();
PreparedStatement p = connection.prepareStatement("SELECT username FROM mar_user WHERE authToken=?");
p.setString(1, token);
ResultSet rs = p.executeQuery();
if (rs.next()) {
return rs.getString("username");
} else {
return null;
}
} catch (SQLException e) {
LogManager.LOGGER.severe("MySQL Error " + e.getErrorCode() + ": " + e.getMessage());
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
byte[] getFloppy(String username) {
Connection connection = null;
try {
connection = getConnection();
PreparedStatement p = connection.prepareStatement("SELECT floppyData FROM mar_user WHERE username=?");
p.setString(1, username);
ResultSet rs = p.executeQuery();
if (rs.next()) {
Blob blob = rs.getBlob("floppyData");
if (blob != null) {
return blob.getBytes(1, (int) blob.length() - 1);
}
}
} catch (SQLException e) {
LogManager.LOGGER.severe("MySQL Error " + e.getErrorCode() + ": " + e.getMessage());
} finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
}

View File

@@ -1,55 +0,0 @@
package net.simon987.server.webserver;
import net.simon987.server.GameServer;
import net.simon987.server.game.World;
import net.simon987.server.logging.LogManager;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
public class TerrainRequestHandler implements MessageHandler {
@Override
public void handle(OnlineUser user, JSONObject json) {
if (json.get("t").equals("terrain") && json.containsKey("x") && json.containsKey("y")) {
// LogManager.LOGGER.fine("Terrain request from " + user.getUser().getUsername());
World world;
try {
world = GameServer.INSTANCE.getGameUniverse().getWorld(
Long.valueOf((long) json.get("x")).intValue(),
Long.valueOf((long) json.get("y")).intValue(), false);
} catch (NullPointerException e) {
LogManager.LOGGER.severe("FIXME: handle TerrainRequestHandler");
return;
}
//todo It might be a good idea to cache this
if (world != null) {
JSONObject response = new JSONObject();
JSONArray terrain = new JSONArray();
int[][] tiles = world.getTileMap().getTiles();
for (int x = 0; x < World.WORLD_SIZE; x++) {
for (int y = 0; y < World.WORLD_SIZE; y++) {
terrain.add(tiles[y][x]);
}
}
response.put("t", "terrain");
response.put("ok", true);
response.put("terrain", terrain);
user.getWebSocket().send(response.toJSONString());
} else {
//Uncharted World
JSONObject response = new JSONObject();
response.put("t", "terrain");
response.put("ok", false);
user.getWebSocket().send(response.toJSONString());
}
}
}
}

View File

@@ -1,86 +0,0 @@
# MySQL username
mysql_user=mar
# MySQL password/
mysql_pass=mar
# MySQL address
mysql_url=jdbc:mysql://localhost:3306/mar?useSSL=false
# File management
save_interval=5
log_limit=2000000
log_count=10
# Web server port
webSocket_port=8887
webSocket_host=0.0.0.0
use_secure_webSocket=0
cert_path=certificates
# ----------------------------------------------
# Length of a tick in ms
tick_length=1000
# Default offset of the origin (starting point of code execution) in words
org_offset=512
# Address of the stack bottom
stack_bottom=65536
# Size of the memory in words
memory_size=65536
# Initial location of new user's controlled unit
new_user_worldX = 32767
new_user_worldY = 32767
# Default user code
new_user_code=; Welcome to Much Assembly required!\n\
; You will find useful information on the game here: https://github.com/simon987/Much-Assembly-Required/wiki\n\
.text\n\
\t; Write code here\n\
\tbrk
# Default held item
new_user_item=0
# ----------------------------------------------
# Biomass units for each blob
biomass_yield=2
# Minimum biomass blob count for the WorldGenerator
minBiomassCount=3
minBiomassRespawnCount=2
# Maximum biomass blob count for the WorldGenerator
maxBiomassCount=10
maxBiomassRespawnCount=6
# Energy generated by consuming biomass
biomassEnergyValue=4000
# Maximum energy of the battery hardware in kJ
battery_max_energy=60000
# Time for biomass respawn in ticks
biomassRespawnTime=64
# Respawn timer will start when biomass count is below this number
biomassRespawnThreshold=1
# NPC lifetime in ticks
npc_lifetime=1024
# Maximum travel distance from the Factory in Worlds
npc_max_factory_distance=3
# Maximum NPC per Factory
factory_max_npc_count=16
# Harvester max hp
harvester_hp_max=100
# Harvester hp regeneration per tick
harvester_regen=5
# Number of biomass units dropped on death
harvester_biomass_drop_count=8
# ----------------------------------------------
# Minimum center point count for the WorldGenerator
wg_centerPointCountMin=5
# Maximum center point count for the WorldGenerator
wg_centerPointCountMax=15
# Wall/Plain tile ratio for the WorldGenerator
wg_wallPlainRatio=4
# Minimum iron tile count for the WorldGenerator
wg_minIronCount=0
# Maximum iron tile count for the WorldGenerator
wg_maxIronCount=2
# Minimum copper tile count for the WorldGenerator
wg_minCopperCount=0
# Maximum copper tile count for the WorldGenerator
wg_maxCopperCount=2
# ----------------------------------------------
# Maximum execution time of user code in ms
user_timeout=100

View File

@@ -1,39 +0,0 @@
package net.simon987.server.assembly;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.assembly.exception.CancelledException;
import net.simon987.server.user.User;
import org.junit.Test;
import java.util.Random;
public class CPUTest {
@Test
public void executeInstruction() throws CancelledException {
ServerConfiguration config = new ServerConfiguration("config.properties");
User user = new User();
CPU cpu = new CPU(config, user);
for(int i = 0 ; i < 3 ; i++){
//Execute every possible instruction with random values in memory
cpu.reset();
cpu.getMemory().clear();
Random random = new Random();
byte[] randomBytes = new byte[cpu.getMemory().getWords().length * 2];
random.nextBytes(randomBytes);
for (int machineCode = Character.MIN_VALUE; machineCode < Character.MAX_VALUE; machineCode++) {
Instruction instruction = cpu.getInstructionSet().get(machineCode & 0x03F); // 0000 0000 00XX XXXX
int source = (machineCode >> 11) & 0x001F; // XXXX X000 0000 0000
int destination = (machineCode >> 6) & 0x001F; // 0000 0XXX XX00 0000
cpu.executeInstruction(instruction, source, destination);
}
}
}
}

View File

@@ -1,45 +0,0 @@
package net.simon987.server.assembly.instruction;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.assembly.*;
import net.simon987.server.user.User;
import org.junit.Test;
public class CallInstructionTest {
@Test
public void execute() throws Exception {
ServerConfiguration config = new ServerConfiguration("config.properties");
int memorySize = config.getInt("memory_size");
//Memory
Memory memory = new Memory(memorySize);
memory.clear();
//RegisterSet
RegisterSet registerSet = new RegisterSet();
registerSet.put(1, new Register("T1"));
registerSet.put(2, new Register("T2"));
registerSet.clear();
//Status
Status status = new Status();
status.clear();
CPU cpu = new CPU(config, new User());
CallInstruction callInstruction = new CallInstruction(cpu);
//We have to check if IP is 'pushed' correctly (try to pop it), the
}
@Test
public void execute1() throws Exception {
}
}

View File

@@ -3,19 +3,25 @@ services:
server:
build:
context: .
command: sh -c "sed -i -e 's#localhost#db#' config.properties && /usr/bin/java -jar /app/target/server-1.2a.jar"
command: sh -c "/usr/bin/java -jar /app/target/server-1.4a.jar"
depends_on:
db:
mongodb:
condition: service_healthy
ports:
- "8887:8887"
db:
image: mysql
healthcheck:
test: ["CMD", "mysqladmin" ,"ping", "-umar", "-pmar"]
environment:
MYSQL_DATABASE: mar
MYSQL_PASSWORD: mar
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
MYSQL_ROOT_PASSWORD: something-secret
MYSQL_USER: mar
- 4567:4567
mongodb:
image: mongo:latest
container_name: "mongodb"
environment:
- MONGO_DATA_DIR=/data/db
- MONGO_LOG_DIR=/dev/null
volumes:
- ./data/db:/data/db
ports:
- 27017:27017
command: mongod --smallfiles --logpath=/dev/null --port 27017
healthcheck:
test: echo 'db.stats().ok' | mongo localhost:27017/mar --quiet
interval: 2s
timeout: 2s
retries: 2

155
pom.xml
View File

@@ -4,14 +4,30 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.simon987.server</groupId>
<artifactId>server_root</artifactId>
<version>1.2a</version>
<packaging>pom</packaging>
<groupId>net.simon987.mar</groupId>
<artifactId>muchassemblyrequired</artifactId>
<version>1.6a</version>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>tsc</executable>
<workingDirectory>./src/main/typescript/</workingDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<version>3.6.2</version>
@@ -22,33 +38,118 @@
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<outputDirectory>../target/plugins</outputDirectory>
<archive>
<manifest>
<addClasspath>false</addClasspath>
<mainClass>net.simon987.server.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
<executions>
<execution>
<id>default-resources</id>
<phase>prepare-package</phase>
<goals>
<goal>resources</goal>
</goals>
<configuration>
<outputDirectory>./target/</outputDirectory>
<resources>
<resource>
<directory>./src/main/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>./target/libs</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<outputDirectory>./target</outputDirectory>
<archive>
<manifest>
<mainClass>net.simon987.mar.server.Main</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>libs/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.7.2</version>
<configuration>
<forkMode>never</forkMode>
<workingDirectory>./src/main/resources</workingDirectory>
</configuration>
</plugin>
</plugins>
</build>
<modules>
<module>Server</module>
<module>Plugin Plant</module>
<module>Plugin NPC</module>
<module>Plugin Misc HW</module>
<module>Plugin Cubot</module>
</modules>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId>
<version>3.9.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.1.11.RELEASE</version>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-template-velocity</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>
<properties>
<!-- explicitly set build encoding so not altered by build platform defaults -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@@ -0,0 +1,94 @@
package net.simon987.mar.biomass;
import net.simon987.mar.server.game.item.Item;
import net.simon987.mar.server.game.objects.GameObject;
import net.simon987.mar.server.game.objects.InventoryHolder;
import org.bson.Document;
import org.json.simple.JSONObject;
public class BiomassBlob extends GameObject implements InventoryHolder {
private static final char MAP_INFO = 0x0101;
/**
* Yield of the blob, in biomass units
*/
private int biomassCount;
public BiomassBlob() {
}
public BiomassBlob(Document document) {
super(document);
biomassCount = document.getInteger("biomassCount");
}
@Override
public char getMapInfo() {
return MAP_INFO;
}
@Override
public JSONObject jsonSerialise() {
JSONObject json = super.jsonSerialise();
json.put("b", biomassCount);
return json;
}
@Override
public Document mongoSerialise() {
Document dbObject = super.mongoSerialise();
dbObject.put("biomassCount", biomassCount);
return dbObject;
}
public int getBiomassCount() {
return biomassCount;
}
public void setBiomassCount(int biomassCount) {
this.biomassCount = biomassCount;
}
/**
* Called when an object attempts to place an item in this BiomassBlob
*
* @return Always returns false
*/
@Override
public boolean placeItem(Item item) {
//Why would you want to place an item in a blob?
return false;
}
@Override
public boolean canTakeItem(int itemId) {
return itemId == ItemBiomass.ID && biomassCount >= 1;
}
/**
* Called when an object attempts to take an item from this BiomassBlob.
* If the object requests biomass, it will be subtracted from biomassCount, and
* if it reaches 0, the plant is deleted
*/
@Override
public void takeItem(int itemId) {
if (itemId == ItemBiomass.ID) {
if (biomassCount > 1) {
biomassCount--;
} else {
//Delete plant
setDead(true);
}
}
}
}

View File

@@ -0,0 +1,36 @@
package net.simon987.mar.biomass;
import net.simon987.mar.server.GameServer;
import net.simon987.mar.server.game.item.Item;
import net.simon987.mar.server.game.objects.ControllableUnit;
import org.bson.Document;
public class ItemBiomass extends Item {
public static final int ID = 0x0001;
private static final int energy = GameServer.INSTANCE.getConfig().getInt("biomassEnergyValue");
@Override
public int getId() {
return ID;
}
public ItemBiomass() {
super(null);
}
public ItemBiomass(Document document) {
super(document);
}
@Override
public void clear(ControllableUnit unit) {
unit.storeEnergy(energy);
}
@Override
public char poll() {
return ID;
}
}

View File

@@ -1,8 +1,9 @@
package net.simon987.biomassplugin;
package net.simon987.mar.biomass;
import net.simon987.server.GameServer;
import net.simon987.server.game.World;
import net.simon987.server.logging.LogManager;
import net.simon987.mar.server.game.world.TileMap;
import net.simon987.mar.server.game.world.TilePlain;
import net.simon987.mar.server.game.world.World;
import org.bson.types.ObjectId;
import java.awt.*;
import java.util.ArrayList;
@@ -21,12 +22,12 @@ public class WorldUtils {
//Count number of plain tiles. If there is less plain tiles than desired amount of blobs,
//set the desired amount of blobs to the plain tile count
int[][] tiles = world.getTileMap().getTiles();
TileMap m = world.getTileMap();
int plainCount = 0;
for (int y = 0; y < World.WORLD_SIZE; y++) {
for (int x = 0; x < World.WORLD_SIZE; x++) {
for (int y = 0; y < world.getWorldSize(); y++) {
for (int x = 0; x < world.getWorldSize(); x++) {
if (tiles[x][y] == 0) {
if (m.getTileIdAt(x, y) == TilePlain.ID) {
plainCount++;
}
}
@@ -39,14 +40,14 @@ public class WorldUtils {
outerLoop:
for (int i = 0; i < blobCount; i++) {
Point p = world.getTileMap().getRandomPlainTile();
Point p = m.getRandomTile(TilePlain.ID);
if (p != null) {
//Don't block worlds
int counter = 0;
while (p.x == 0 || p.y == 0 || p.x == World.WORLD_SIZE - 1 || p.y == World.WORLD_SIZE - 1 ||
while (p.x == 0 || p.y == 0 || p.x == world.getWorldSize() - 1 || p.y == world.getWorldSize() - 1 ||
world.getGameObjectsAt(p.x, p.y).size() != 0) {
p = world.getTileMap().getRandomPlainTile();
p = m.getRandomTile(TilePlain.ID);
counter++;
if (counter > 25) {
@@ -62,8 +63,7 @@ public class WorldUtils {
}
BiomassBlob biomassBlob = new BiomassBlob();
biomassBlob.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
// biomassBlob.setStyle(0); //TODO: set style depending on difficulty level? or random? from config?
biomassBlob.setObjectId(new ObjectId());
biomassBlob.setBiomassCount(yield);
biomassBlob.setX(p.x);
biomassBlob.setY(p.y);
@@ -73,9 +73,6 @@ public class WorldUtils {
}
}
LogManager.LOGGER.info("Generated " + biomassBlobs.size() + " biomassBlobs for World (" + world.getX() + ',' +
world.getY() + ')');
return biomassBlobs;
}
}

View File

@@ -1,25 +1,23 @@
package net.simon987.biomassplugin.event;
package net.simon987.mar.biomass.event;
import net.simon987.biomassplugin.BiomassBlob;
import net.simon987.server.GameServer;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.ObjectDeathEvent;
import net.simon987.server.game.GameObject;
import net.simon987.server.game.World;
import net.simon987.server.logging.LogManager;
import net.simon987.mar.biomass.BiomassBlob;
import net.simon987.mar.server.IServerConfiguration;
import net.simon987.mar.server.event.GameEvent;
import net.simon987.mar.server.event.GameEventListener;
import net.simon987.mar.server.event.ObjectDeathEvent;
import net.simon987.mar.server.game.objects.GameObject;
import net.simon987.mar.server.game.world.World;
import org.bson.types.ObjectId;
/**
* Handles ObjectDeathEvent events
*/
public class ObjectDeathListener implements GameEventListener {
private int biomassDropCount;
private final int biomassDropCount;
public ObjectDeathListener(ServerConfiguration config) {
public ObjectDeathListener(IServerConfiguration config) {
biomassDropCount = config.getInt("harvester_biomass_drop_count");
}
@Override
@@ -29,22 +27,19 @@ public class ObjectDeathListener implements GameEventListener {
@Override
public void handle(GameEvent event) {
// TODO: setup enum with all GameObject type IDs
if (((ObjectDeathEvent) event).getSourceObjectId() == 10) {
//An HarvesterNPC ObjectDeathEvent is received
GameObject dyingHarvesterNPC = (GameObject)event.getSource();
if (event.getSource().getClass().getCanonicalName().equals("net.simon987.mar.npc.HarvesterNPC")) {
//An HarvesterNPC ObjectDeathEvent is received
GameObject dyingHarvesterNPC = (GameObject) event.getSource();
//Don't spawn biomass on World border
if (dyingHarvesterNPC.getX() != 0 && dyingHarvesterNPC.getX() != World.WORLD_SIZE - 1 &&
dyingHarvesterNPC.getY() != 0 && dyingHarvesterNPC.getY() != World.WORLD_SIZE - 1) {
if (dyingHarvesterNPC.getX() != 0 && dyingHarvesterNPC.getX() != dyingHarvesterNPC.getWorld().getWorldSize() - 1 &&
dyingHarvesterNPC.getY() != 0 && dyingHarvesterNPC.getY() != dyingHarvesterNPC.getWorld().getWorldSize() - 1) {
//Create a new biomass
BiomassBlob newBiomassBlob = createBiomassBlobAt(
dyingHarvesterNPC.getX(), dyingHarvesterNPC.getY(), dyingHarvesterNPC.getWorld());
//Add it to the world game objects
dyingHarvesterNPC.getWorld().addObject(newBiomassBlob);
LogManager.LOGGER.fine("Spawned biomass at (" + newBiomassBlob.getX() +
", " + newBiomassBlob.getY() + ")");
}
}
}
@@ -59,8 +54,7 @@ public class ObjectDeathListener implements GameEventListener {
private BiomassBlob createBiomassBlobAt(int x, int y, World world) {
BiomassBlob biomassBlob = new BiomassBlob();
biomassBlob.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
// biomassBlob.setStyle(0); //TODO: set style depending on difficulty level? or random? from config?
biomassBlob.setObjectId(new ObjectId());
biomassBlob.setBiomassCount(biomassDropCount);
biomassBlob.setX(x);
biomassBlob.setY(y);

View File

@@ -1,11 +1,11 @@
package net.simon987.biomassplugin.event;
package net.simon987.mar.biomass.event;
import net.simon987.biomassplugin.BiomassBlob;
import net.simon987.biomassplugin.WorldUtils;
import net.simon987.server.GameServer;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.WorldGenerationEvent;
import net.simon987.mar.biomass.BiomassBlob;
import net.simon987.mar.biomass.WorldUtils;
import net.simon987.mar.server.GameServer;
import net.simon987.mar.server.event.GameEvent;
import net.simon987.mar.server.event.GameEventListener;
import net.simon987.mar.server.event.WorldGenerationEvent;
import java.util.ArrayList;

View File

@@ -0,0 +1,75 @@
package net.simon987.mar.biomass.event;
import net.simon987.mar.biomass.BiomassBlob;
import net.simon987.mar.biomass.WorldUtils;
import net.simon987.mar.server.GameServer;
import net.simon987.mar.server.IServerConfiguration;
import net.simon987.mar.server.event.GameEvent;
import net.simon987.mar.server.event.GameEventListener;
import net.simon987.mar.server.event.WorldUpdateEvent;
import net.simon987.mar.server.game.world.World;
import java.util.ArrayList;
import java.util.HashMap;
public class WorldUpdateListener implements GameEventListener {
private final HashMap<World, Long> worldWaitMap = new HashMap<>(200);
private static int minBlobCount;
private static int maxBlobCount;
private static int blobYield;
private static int waitTime;
private static int blobThreshold;
public WorldUpdateListener(IServerConfiguration config) {
minBlobCount = config.getInt("minBiomassRespawnCount");
maxBlobCount = config.getInt("maxBiomassRespawnCount");
waitTime = config.getInt("biomassRespawnTime");
blobThreshold = config.getInt("biomassRespawnThreshold");
blobYield = config.getInt("biomass_yield");
}
@Override
public Class getListenedEventType() {
return WorldUpdateEvent.class;
}
@Override
public void handle(GameEvent event) {
World world = ((WorldUpdateEvent) event).getWorld();
if (world.getDimension().startsWith("w")) {
//If there is less than the respawn threshold,
if (world.findObjects(BiomassBlob.class).size() < blobThreshold) {
//Set a timer for respawn_time ticks
if (!worldWaitMap.containsKey(world) || worldWaitMap.get(world) == 0L) {
worldWaitMap.put(world, GameServer.INSTANCE.getUniverse().getTime() + waitTime);
} else {
long waitUntil = worldWaitMap.get(world);
if (GameServer.INSTANCE.getUniverse().getTime() >= waitUntil) {
//If the timer was set less than respawn_time ticks ago, respawn the blobs
ArrayList<BiomassBlob> newBlobs = WorldUtils.generateBlobs(world, minBlobCount,
maxBlobCount, blobYield);
for (BiomassBlob blob : newBlobs) {
world.addObject(blob);
world.incUpdatable();
}
//Set the 'waitUntil' time to 0 to indicate that we are not waiting
worldWaitMap.replace(world, 0L);
}
}
}
}
}
}

View File

@@ -0,0 +1,111 @@
package net.simon987.mar.construction;
import net.simon987.mar.server.game.item.Item;
import net.simon987.mar.server.game.objects.GameObject;
import net.simon987.mar.server.game.objects.InventoryHolder;
import net.simon987.mar.server.io.JSONSerializable;
import net.simon987.mar.server.io.MongoSerializable;
import org.bson.Document;
import org.json.simple.JSONObject;
import java.util.HashMap;
import java.util.Map;
public abstract class BluePrint implements InventoryHolder, JSONSerializable, MongoSerializable {
/**
* Map of items id and required amount
* <br>The amount is decremented as the items are added
*/
protected Map<Integer, Integer> requiredItems;
/**
* This object will be instantiated when completed
*/
protected Class<? extends GameObject> targetObject;
/**
* Set to true when all the requirements are met
*/
private boolean completed;
public BluePrint() {
requiredItems = new HashMap<>();
}
public BluePrint(Document document) {
requiredItems = (Map<Integer, Integer>) document.get("required_items");
completed = document.getBoolean("completed");
try {
targetObject = Class.forName(document.getString("target")).asSubclass(GameObject.class);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private void checkCompleted() {
for (Integer remaining : requiredItems.values()) {
if (remaining > 0) {
return;
}
}
completed = true;
}
@Override
public boolean placeItem(Item item) {
if (requiredItems.containsKey(item.getId()) && requiredItems.get(item.getId()) > 0) {
requiredItems.put(item.getId(), requiredItems.get(item.getId()) - 1);
checkCompleted();
return true;
}
return false;
}
@Override
public void takeItem(int itemId) {
}
@Override
public boolean canTakeItem(int itemId) {
return false;
}
public boolean isCompleted() {
return completed;
}
public Class<? extends GameObject> getTargetObject() {
return targetObject;
}
@Override
public JSONObject debugJsonSerialise() {
return jsonSerialise();
}
@Override
public JSONObject jsonSerialise() {
JSONObject json = new JSONObject();
json.put("target", targetObject);
json.put("required_items", requiredItems);
return json;
}
@Override
public Document mongoSerialise() {
Document document = new Document();
document.put("completed", completed);
document.put("target", targetObject);
document.put("required_items", requiredItems);
return document;
}
}

View File

@@ -0,0 +1,61 @@
package net.simon987.mar.construction;
import net.simon987.mar.server.logging.LogManager;
import org.bson.Document;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
public class BluePrintRegistry {
public static final BluePrintRegistry INSTANCE = new BluePrintRegistry();
private final Map<String, Class<? extends BluePrint>> blueprints;
private final Map<String, String> digitizedBlueprints;
private BluePrintRegistry() {
blueprints = new HashMap<>();
digitizedBlueprints = new HashMap<>();
}
public void registerBluePrint(Class<? extends BluePrint> clazz) {
blueprints.put(clazz.getCanonicalName(), clazz);
String bpData = new String(BluePrintUtil.bluePrintData(clazz));
digitizedBlueprints.put(bpData, clazz.getCanonicalName());
}
public BluePrint deserializeBlueprint(Document document) {
String type = document.getString("type");
if (blueprints.containsKey(type)) {
try {
return blueprints.get(type).getConstructor(Document.class).newInstance(document);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException e) {
e.printStackTrace();
return null;
} catch (InvocationTargetException e) {
LogManager.LOGGER.severe("(Construction Plugin) Error while trying to deserialize object of type " + type + ": " + e.getTargetException().getMessage());
LogManager.LOGGER.severe(document.toJson());
e.getTargetException().printStackTrace();
return null;
}
} else {
LogManager.LOGGER.severe("(Construction Plugin) Trying to deserialize unknown BluePrint type: " + type);
return null;
}
}
public BluePrint deserializeBluePrint(char[] chars) {
String bpData = new String(chars);
if (digitizedBlueprints.containsKey(bpData)) {
return deserializeBlueprint(new Document("type", digitizedBlueprints.get(bpData)));
}
return null;
}
}

View File

@@ -0,0 +1,66 @@
package net.simon987.mar.construction;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
class BluePrintUtil {
private static byte[] secretKey;
private static final String SHA512 = "SHA-512";
//We need 1024 chars = 2048 bytes = 32 values
private static final char[] ARBITRARY_STRING = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456".toCharArray();
static void setSecretKey(String secretKey) {
BluePrintUtil.secretKey = secretKey.getBytes(StandardCharsets.UTF_8);
}
/**
* Hash a message using SHA512 with the server secret key as the sal
*
* @return 128-bit char array
*/
private static char[] hashMessage(String message) {
try {
MessageDigest md = MessageDigest.getInstance(SHA512);
md.update(secretKey);
md.update(message.getBytes(StandardCharsets.UTF_8));
byte[] digest = md.digest();
char[] chars = new char[digest.length / 2];
ByteBuffer.wrap(digest).order(ByteOrder.BIG_ENDIAN).asCharBuffer().get(chars);
return chars;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
/**
* Returns a char array representation of a blueprint. It is obtained by hashing the blueprint
* properties with the server secret key. Some arbitrary values are added to make a 1024-char
* array. The same blueprint and secret key always gives the same result.
*/
static char[] bluePrintData(Class<? extends BluePrint> blueprint) {
char[] result = new char[ARBITRARY_STRING.length * 32];
for (int i = ARBITRARY_STRING.length - 1; i > 0; --i) {
char[] hashedBlueprint = hashMessage(ARBITRARY_STRING[i] + blueprint.getName());
if (hashedBlueprint != null) {
System.arraycopy(hashedBlueprint, 0, result,
i * hashedBlueprint.length, hashedBlueprint.length);
}
}
return result;
}
}

View File

@@ -0,0 +1,12 @@
package net.simon987.mar.construction;
import net.simon987.mar.server.GameServer;
public class ConstructionPlugin {
public void init(GameServer gameServer) {
// TODO
BluePrintUtil.setSecretKey(gameServer.getSecretKey());
BluePrintRegistry.INSTANCE.registerBluePrint(ObstacleBlueprint.class);
}
}

View File

@@ -0,0 +1,82 @@
package net.simon987.mar.construction;
import net.simon987.mar.server.GameServer;
import net.simon987.mar.server.game.item.Item;
import net.simon987.mar.server.game.objects.InventoryHolder;
import net.simon987.mar.server.game.objects.Structure;
import net.simon987.mar.server.game.objects.Updatable;
import org.bson.Document;
import org.json.simple.JSONObject;
public class ConstructionSite extends Structure implements Updatable, InventoryHolder {
public static final int MAP_INFO = 0xFFFF; //TODO: determine
public static final int LIFETIME = GameServer.INSTANCE.getConfig().getInt("construction_site_ttl");
private int age;
private final BluePrint bluePrint;
public ConstructionSite(BluePrint bluePrint) {
super(1, 1);
this.bluePrint = bluePrint;
this.age = 0;
}
public ConstructionSite(Document document) {
super(document, 1, 1);
age = document.getInteger("age");
bluePrint = BluePrintRegistry.INSTANCE.deserializeBlueprint((Document) document.get("blueprint"));
}
@Override
public char getMapInfo() {
return MAP_INFO;
}
@Override
public void update() {
age += 1;
if (age > LIFETIME) {
setDead(true);
}
}
@Override
public boolean placeItem(Item item) {
return bluePrint.placeItem(item);
}
@Override
public void takeItem(int itemId) {
//NOOP
}
@Override
public boolean canTakeItem(int itemId) {
return false;
}
@Override
public JSONObject jsonSerialise() {
JSONObject json = super.jsonSerialise();
json.put("blueprint", bluePrint.jsonSerialise());
json.put("age", age);
return json;
}
@Override
public Document mongoSerialise() {
Document document = super.mongoSerialise();
document.put("blueprint", bluePrint.mongoSerialise());
document.put("age", age);
return document;
}
}

View File

@@ -0,0 +1,58 @@
package net.simon987.mar.construction;
import net.simon987.mar.server.assembly.Memory;
import net.simon987.mar.server.game.item.Item;
import org.bson.Document;
import org.json.simple.JSONObject;
public class ItemBluePrint extends Item {
public static final int ID = 0x0005;
private Class<? extends BluePrint> bluePrint;
public ItemBluePrint() {
super(null);
}
public ItemBluePrint(Document document) {
super(document);
try {
bluePrint = Class.forName(document.getString("blueprint")).asSubclass(BluePrint.class);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@Override
public void digitize(Memory memory, int offset) {
char[] data = BluePrintUtil.bluePrintData(bluePrint);
memory.write(offset, data, 0, data.length);
System.out.println("DEBUG: blueprint digitize " + data.length);
}
@Override
public char poll() {
return ID;
}
@Override
public int getId() {
return ID;
}
@Override
public JSONObject debugJsonSerialise() {
JSONObject json = super.debugJsonSerialise();
json.put("blueprint", bluePrint.getCanonicalName());
return json;
}
@Override
public Document mongoSerialise() {
Document document = super.mongoSerialise();
document.put("blueprint", bluePrint.getCanonicalName());
return document;
}
}

View File

@@ -0,0 +1,104 @@
package net.simon987.mar.construction;
import net.simon987.mar.server.GameServer;
import net.simon987.mar.server.game.objects.Attackable;
import net.simon987.mar.server.game.objects.Structure;
import net.simon987.mar.server.game.objects.Updatable;
import org.bson.Document;
import org.json.simple.JSONObject;
public class Obstacle extends Structure implements Attackable, Updatable {
public static final int MAP_INFO = 0x0601;
private static final int HEAL_RATE = GameServer.INSTANCE.getConfig().getInt("obstacle_regen");
private static final int MAX_HP = GameServer.INSTANCE.getConfig().getInt("obstacle_hp");
private int hp;
private int color;
public Obstacle() {
super(1, 1);
}
public Obstacle(Document document) {
super(document, 1, 1);
hp = document.getInteger(hp);
color = document.getInteger(color);
}
@Override
public void update() {
heal(HEAL_RATE);
}
@Override
public void setHealRate(int hp) {
}
@Override
public int getHp() {
return hp;
}
@Override
public void setHp(int hp) {
this.hp = hp;
}
@Override
public int getMaxHp() {
return MAX_HP;
}
@Override
public void setMaxHp(int hp) {
}
@Override
public void heal(int amount) {
hp = Math.min(getMaxHp(), hp + amount);
}
@Override
public void damage(int amount) {
hp -= amount;
if (hp < 0) {
setDead(true);
}
}
@Override
public char getMapInfo() {
return MAP_INFO;
}
@Override
public Document mongoSerialise() {
Document document = super.mongoSerialise();
document.put("hp", hp);
document.put("color", hp);
return document;
}
@Override
public JSONObject debugJsonSerialise() {
return jsonSerialise();
}
@Override
public JSONObject jsonSerialise() {
JSONObject json = super.jsonSerialise();
json.put("hp", hp);
json.put("color", hp);
return json;
}
}

View File

@@ -0,0 +1,19 @@
package net.simon987.mar.construction;
import net.simon987.mar.server.game.item.ItemIron;
import org.bson.Document;
public class ObstacleBlueprint extends BluePrint {
public ObstacleBlueprint() {
super();
this.requiredItems.put(ItemIron.ID, 2); //TODO: load from config?
this.targetObject = Obstacle.class;
}
public ObstacleBlueprint(Document document) {
this.requiredItems.put(ItemIron.ID, 2); //TODO: load from config?
this.targetObject = Obstacle.class;
}
}

View File

@@ -0,0 +1,52 @@
package net.simon987.mar.cubot;
import net.simon987.mar.server.GameServer;
import net.simon987.mar.server.assembly.HardwareModule;
import net.simon987.mar.server.assembly.Status;
import net.simon987.mar.server.assembly.Util;
import net.simon987.mar.server.game.objects.ControllableUnit;
import org.bson.Document;
/**
* Hardware to get game time
*/
public class Clock extends HardwareModule {
private static final char HWID = 0x0008;
public static final char DEFAULT_ADDRESS = 0x0008;
public Clock() {
}
public Clock(Document document, ControllableUnit unit) {
super(document, unit);
}
@Override
public void handleInterrupt(Status status) {
int time = (int) GameServer.INSTANCE.getUniverse().getTime();
//Will need to be changed to quadword in about 136 years
getCpu().getRegisterSet().getRegister("B").setValue(Util.getHigherWord(time));
getCpu().getRegisterSet().getRegister("C").setValue(Util.getLowerWord(time));
}
@Override
public char getId() {
return HWID;
}
@Override
public Document mongoSerialise() {
Document dbObject = new Document();
dbObject.put("type", getClass().getCanonicalName());
return dbObject;
}
}

View File

@@ -0,0 +1,610 @@
package net.simon987.mar.cubot;
import net.simon987.mar.cubot.event.CubotWalkEvent;
import net.simon987.mar.cubot.event.DeathEvent;
import net.simon987.mar.server.GameServer;
import net.simon987.mar.server.IServerConfiguration;
import net.simon987.mar.server.assembly.CPU;
import net.simon987.mar.server.assembly.HardwareModule;
import net.simon987.mar.server.assembly.Memory;
import net.simon987.mar.server.assembly.Status;
import net.simon987.mar.server.assembly.exception.CancelledException;
import net.simon987.mar.server.event.GameEvent;
import net.simon987.mar.server.game.item.Item;
import net.simon987.mar.server.game.item.ItemVoid;
import net.simon987.mar.server.game.objects.*;
import net.simon987.mar.server.user.User;
import org.bson.Document;
import org.json.simple.JSONObject;
import java.awt.*;
import java.util.List;
import java.util.*;
public class Cubot extends GameObject implements Updatable, ControllableUnit, MessageReceiver {
private static final char MAP_INFO = 0x0200;
/**
* Hit points
*/
private int hp;
/**
* Maximum hit points
*/
private int maxHp;
/**
* Shield points
*/
private int shield;
/**
* Maximum shield points
*/
private int maxShield;
/**
* Action that was set during the current tick. It is set to IDLE by default
*/
private Action currentAction = Action.IDLE;
/**
* Action at the end of the last tick
*/
private Action lastAction = Action.IDLE;
/**
* Status bit field that was set during the current tick. It is set to 0 by default
* <br>See CubotStatus and addStatus() method
*/
private char currentStatus;
/**
* Status bit field at the end of the last tick
*/
private char lastStatus;
/**
* Buffer of keypress codes. It is not changed between ticks and it is reset when
* the player uploads their code
*/
private ArrayList<Integer> keyboardBuffer = new ArrayList<>();
/**
* Buffer of console messages (also called 'internal buffer') that was set during the current tick
*/
private final ArrayList<char[]> consoleMessagesBuffer = new ArrayList<>(CONSOLE_BUFFER_MAX_SIZE);
/**
* Buffer of console messages (also called 'internal buffer') at the end of the last tick
*/
private ArrayList<char[]> lastConsoleMessagesBuffer = new ArrayList<>(CONSOLE_BUFFER_MAX_SIZE);
/**
* Console mode that was set during the current tick. It is set to NORMAL by default
*/
private ConsoleMode consoleMode = ConsoleMode.NORMAL;
/**
* Console mode at the end of the last tick
*/
private ConsoleMode lastConsoleMode = ConsoleMode.NORMAL;
/**
* User that controls this Cubot
*/
private User parent;
/**
* Maximum size of the console buffer (also called 'internal buffer')
*/
public static final int CONSOLE_BUFFER_MAX_SIZE = 40;
/**
* List of attached hardware, 'modules'
*/
private final Map<Integer, HardwareModule> hardwareAddresses = new HashMap<>();
private final Map<Class<? extends HardwareModule>, Integer> hardwareModules = new HashMap<>();
/**
* Cubot's brain box
*/
private CPU cpu;
public enum ConsoleMode {
/**
* Used by the ComPort hardware - clears the console screen (client-side)
*/
CLEAR,
/**
* No specific client-side action
*/
NORMAL
}
public Cubot() {
}
public Cubot(Document document) {
super(document);
hp = document.getInteger("hp");
shield = document.getInteger("shield");
setDirection(Direction.getDirection(document.getInteger("direction")));
IServerConfiguration config = GameServer.INSTANCE.getConfig();
maxHp = config.getInt("cubot_max_hp");
maxShield = config.getInt("cubot_max_shield");
try {
cpu = CPU.deserialize((Document) document.get("cpu"), this);
ArrayList hardwareList = (ArrayList) document.get("hardware");
for (Object serialisedHw : hardwareList) {
HardwareModule hardware = GameServer.INSTANCE.getRegistry().deserializeHardware((Document) serialisedHw, this);
hardware.setCpu(cpu);
attachHardware(hardware, ((Document) serialisedHw).getInteger("address"));
}
} catch (CancelledException e) {
e.printStackTrace();
}
}
@Override
public char getMapInfo() {
return MAP_INFO;
}
@Override
public void update() {
if (getCpu().isPaused() || getCpu().isExecuting()) {
return;
}
if (currentAction == Action.WALKING) {
if (spendEnergy(100)) {
if (!incrementLocation()) {
//Couldn't walk
currentAction = Action.IDLE;
}else{
GameServer.INSTANCE.getEventDispatcher().dispatch(new CubotWalkEvent(this));
}
} else {
currentAction = Action.IDLE;
}
}
/*
* CurrentAction is set during the code execution and this function is called right after
* If no action as been set, the action sent to the client is the action in currentAction that
* was set last tick (IDLE)
*/
lastAction = currentAction;
currentAction = Action.IDLE;
//Same principle for hologram
//And the console
lastConsoleMode = consoleMode;
consoleMode = ConsoleMode.NORMAL;
lastConsoleMessagesBuffer = new ArrayList<>(consoleMessagesBuffer);
consoleMessagesBuffer.clear();
//And the status..
lastStatus = currentStatus;
currentStatus = 0;
for (HardwareModule module : hardwareAddresses.values()) {
module.update();
}
}
@Override
public JSONObject jsonSerialise() {
JSONObject json = super.jsonSerialise();
json.put("direction", getDirection().ordinal());
CubotInventory inv = (CubotInventory) getHardware(CubotInventory.class);
int heldItem = inv.getInventory().getOrDefault(inv.getPosition(), new ItemVoid()).getId();
json.put("heldItem", heldItem);
json.put("hp", hp);
json.put("shield", shield);
json.put("action", lastAction.ordinal());
if (parent != null) {
json.put("parent", parent.getUsername()); //Only used client-side for now
}
for (HardwareModule module : hardwareAddresses.values()) {
JSONObject hwJson = module.jsonSerialise();
if (hwJson != null) {
json.put(module.getClass().getName(), hwJson);
}
}
return json;
}
@Override
public Document mongoSerialise() {
Document dbObject = super.mongoSerialise();
dbObject.put("direction", getDirection().ordinal());
dbObject.put("hp", hp);
dbObject.put("shield", shield);
dbObject.put("action", lastAction.ordinal());
if (parent != null) {
dbObject.put("parent", parent.getUsername()); //Only used client-side for now
}
List<Document> hardwareList = new ArrayList<>();
for (Integer address : hardwareAddresses.keySet()) {
HardwareModule hardware = hardwareAddresses.get(address);
Document serialisedHw = hardware.mongoSerialise();
serialisedHw.put("address", address);
hardwareList.add(serialisedHw);
}
dbObject.put("hardware", hardwareList);
dbObject.put("cpu", cpu.mongoSerialise());
return dbObject;
}
/**
* Reset to 'factory settings', as it were when it was first created
*/
private void reset() {
setDead(false);
setHp(maxHp);
setShield(0);
setEnergy(((CubotBattery) getHardware(CubotBattery.class)).getMaxEnergy());
clearKeyboardBuffer();
consoleMessagesBuffer.clear();
lastConsoleMessagesBuffer.clear();
currentStatus = 0;
lastStatus = 0;
addStatus(CubotStatus.FACTORY_NEW);
for (HardwareModule module : hardwareAddresses.values()) {
module.reset();
}
}
@Override
public boolean onDeadCallback() {
GameEvent event = new DeathEvent(this);
GameServer.INSTANCE.getEventDispatcher().dispatch(event);
reset();
//Teleport to spawn point
this.getWorld().removeObject(this);
this.getWorld().decUpdatable();
IServerConfiguration config = GameServer.INSTANCE.getConfig();
Random random = new Random();
int spawnX = config.getInt("new_user_worldX") + random.nextInt(5);
int spawnY = config.getInt("new_user_worldY") + random.nextInt(5);
String dimension = config.getString("new_user_dimension");
this.setWorld(GameServer.INSTANCE.getUniverse().getWorld(spawnX, spawnY, true, dimension));
Point point = this.getWorld().getRandomPassableTile();
this.setX(point.x);
this.setY(point.y);
this.getWorld().addObject(this);
this.getWorld().incUpdatable();
return true;
}
@Override
public void setKeyboardBuffer(ArrayList<Integer> kbBuffer) {
keyboardBuffer = kbBuffer;
}
@Override
public ArrayList<Integer> getKeyboardBuffer() {
return keyboardBuffer;
}
public void clearKeyboardBuffer() {
keyboardBuffer.clear();
}
public void setCurrentAction(Action currentAction) {
this.currentAction = currentAction;
}
public void setParent(User parent) {
this.parent = parent;
}
@Override
public User getParent() {
return parent;
}
public Action getAction() {
return lastAction;
}
public Action getCurrentAction() {
return currentAction;
}
public int getEnergy() {
CubotBattery battery = (CubotBattery) getHardware(CubotBattery.class);
return battery.getEnergy();
}
public void setEnergy(int energy) {
CubotBattery battery = (CubotBattery) getHardware(CubotBattery.class);
battery.setEnergy(energy);
}
public boolean spendEnergy(int amount) {
CubotBattery battery = (CubotBattery) getHardware(CubotBattery.class);
if (battery.getEnergy() - amount < 0) {
return false;
} else {
battery.setEnergy(battery.getEnergy() - amount);
return true;
}
}
public void storeEnergy(int amount) {
CubotBattery battery = (CubotBattery) getHardware(CubotBattery.class);
battery.setEnergy(Math.min(battery.getEnergy() + amount, battery.getMaxEnergy()));
}
public void setMaxEnergy(int maxEnergy) {
CubotBattery battery = (CubotBattery) getHardware(CubotBattery.class);
battery.setMaxEnergy(maxEnergy);
}
public int getMaxEnergy() {
CubotBattery battery = (CubotBattery) getHardware(CubotBattery.class);
return battery.getMaxEnergy();
}
public int getShield() {
return shield;
}
public void setShield(int shield) {
this.shield = shield;
}
public boolean chargeShield(int amount) {
amount = Math.min(amount, maxShield - shield);
int energySpent = amount * CubotShield.COST;
if(spendEnergy(energySpent)) {
shield += amount;
return true;
} else {
return false;
}
}
/**
* Damages shield by amount.
*
* Return damage that broke through the shield.
*/
public int damageShield(int amount) {
int after = shield - amount;
if(after < 0) {
shield = 0;
return -after;
}
shield = after;
return 0;
}
@Override
public Memory getFloppyData() {
CubotFloppyDrive drive = (CubotFloppyDrive) getHardware(CubotFloppyDrive.class);
if (drive.getFloppy() != null) {
return drive.getFloppy().getMemory();
} else {
return null;
}
}
@Override
public boolean isAt(int x, int y) {
return false;
}
@Override
public void setAction(Action action) {
currentAction = action;
}
@Override
public boolean sendMessage(char[] message) {
if (consoleMessagesBuffer.size() < CONSOLE_BUFFER_MAX_SIZE) {
consoleMessagesBuffer.add(message);
return true;
} else {
return false;
}
}
public ArrayList<char[]> getConsoleMessagesBuffer() {
return lastConsoleMessagesBuffer;
}
public int getConsoleMode() {
return lastConsoleMode.ordinal();
}
public void setConsoleMode(ConsoleMode consoleMode) {
this.consoleMode = consoleMode;
}
public void addStatus(CubotStatus status) {
currentStatus |= status.val;
}
public void removeStatus(CubotStatus status) {
currentStatus &= (~status.val);
}
public char getStatus() {
return lastStatus;
}
/**
* Currently has no effect
*/
@Override
public void setHealRate(int hp) {
//no op
}
@Override
public int getHp() {
return hp;
}
@Override
public void setHp(int hp) {
this.hp = hp;
}
@Override
public int getMaxHp() {
return maxHp;
}
@Override
public void setMaxHp(int hp) {
this.maxHp = hp;
}
public int getMaxShield() {
return maxShield;
}
public void setMaxShield(int maxShield) {
this.maxShield = maxShield;
}
@Override
public void heal(int amount) {
hp += amount;
//Can't heal above max
if (hp > maxHp) {
hp = maxHp;
}
}
@Override
public void damage(int amount) {
//Damage shield first
int hullDamage = damageShield(amount);
hp -= hullDamage;
if (hp <= 0) {
setDead(true);
}
}
@Override
public void attachHardware(HardwareModule hardware, int address) {
hardwareAddresses.put(address, hardware);
hardwareModules.put(hardware.getClass(), address);
}
@Override
public void detachHardware(int address) {
hardwareAddresses.remove(address);
Class<? extends HardwareModule> toRemove = null;
for (Class<? extends HardwareModule> clazz : hardwareModules.keySet()) {
if (hardwareModules.get(clazz) == address) {
toRemove = clazz;
}
}
hardwareModules.remove(toRemove);
}
@Override
public boolean hardwareInterrupt(int address, Status status) {
HardwareModule hardware = hardwareAddresses.get(address);
if (hardware != null) {
hardware.handleInterrupt(status);
return true;
} else {
return false;
}
}
@Override
public int hardwareQuery(int address) {
HardwareModule hardware = hardwareAddresses.get(address);
if (hardware != null) {
return hardware.getId();
} else {
return 0;
}
}
public HardwareModule getHardware(Class<? extends HardwareModule> clazz) {
return hardwareAddresses.get(hardwareModules.get(clazz));
}
public HardwareModule getHardware(int address) {
return hardwareAddresses.get(address);
}
@Override
public CPU getCpu() {
return cpu;
}
public void setCpu(CPU cpu) {
this.cpu = cpu;
}
@Override
public void giveItem(Item item) {
//Overwrite item at current position
((CubotInventory) getHardware(CubotInventory.class)).putItem(item);
}
@Override
public String toString() {
StringBuilder str = new StringBuilder(super.toString());
str.append("\nHardware: \n");
for (Integer i : hardwareAddresses.keySet()) {
str.append(String.format("%04X", i)).append(":").append(hardwareAddresses.get(i)).append("\n");
}
return str.toString();
}
}

View File

@@ -0,0 +1,120 @@
package net.simon987.mar.cubot;
import net.simon987.mar.server.GameServer;
import net.simon987.mar.server.assembly.HardwareModule;
import net.simon987.mar.server.assembly.Status;
import net.simon987.mar.server.game.objects.ControllableUnit;
import org.bson.Document;
import org.json.simple.JSONObject;
public class CubotBattery extends HardwareModule {
public static final int DEFAULT_ADDRESS = 0x000A;
/**
* Hardware ID (Should be unique)
*/
public static final char HWID = 0x000A;
/**
* Solar panel multiplier
* <br>TODO: Set this constant in dimension
*/
private static final float SOLAR_PANEL_MULTIPLIER = 1;
/**
* Energy units in kJ
*/
private int energy;
/**
* Maximum energy units in kJ
*/
private int maxEnergy;
private static final int BATTERY_POLL = 1;
private static final int BATTERY_GET_MAX_CAPACITY = 2;
public CubotBattery(ControllableUnit unit) {
super(null, unit);
energy = GameServer.INSTANCE.getConfig().getInt("battery_max_energy");
maxEnergy = GameServer.INSTANCE.getConfig().getInt("battery_max_energy");
}
public CubotBattery(Document document, ControllableUnit cubot) {
super(document, cubot);
energy = document.getInteger("energy");
maxEnergy = document.getInteger("max_energy");
}
@Override
public void handleInterrupt(Status status) {
int a = getCpu().getRegisterSet().getRegister("A").getValue();
if (a == BATTERY_POLL) {
getCpu().getRegisterSet().getRegister("B").setValue(energy);
} else if (a == BATTERY_GET_MAX_CAPACITY) {
getCpu().getRegisterSet().getRegister("B").setValue(maxEnergy);
}
}
@Override
public char getId() {
return HWID;
}
@Override
public JSONObject jsonSerialise() {
JSONObject json = new JSONObject();
json.put("energy", energy);
return json;
}
@Override
public JSONObject debugJsonSerialise() {
JSONObject json = jsonSerialise();
json.put("max_energy", maxEnergy);
return json;
}
@Override
public Document mongoSerialise() {
Document document = super.mongoSerialise();
document.put("energy", energy);
document.put("max_energy", maxEnergy);
return document;
}
public int getEnergy() {
return energy;
}
public void setEnergy(int energy) {
this.energy = energy;
}
public int getMaxEnergy() {
return maxEnergy;
}
public void setMaxEnergy(int maxEnergy) {
this.maxEnergy = maxEnergy;
}
@Override
public void update() {
energy = Math.min(maxEnergy,
energy + (int) (SOLAR_PANEL_MULTIPLIER * GameServer.INSTANCE.getDayNightCycle().getSunIntensity()));
}
}

View File

@@ -1,31 +1,32 @@
package net.simon987.cubotplugin;
package net.simon987.mar.cubot;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status;
import net.simon987.server.game.GameObject;
import net.simon987.server.game.Programmable;
import net.simon987.mar.server.assembly.HardwareModule;
import net.simon987.mar.server.assembly.Status;
import net.simon987.mar.server.game.objects.ControllableUnit;
import net.simon987.mar.server.game.objects.GameObject;
import net.simon987.mar.server.game.objects.MessageReceiver;
import org.bson.Document;
import java.awt.*;
import java.util.ArrayList;
public class CubotComPort extends CpuHardware {
public class CubotComPort extends HardwareModule {
public static final char HWID = 0xD;
public static final int DEFAULT_ADDRESS = 0xD;
private Cubot cubot;
private static final int COMPORT_BUFFER_CLEAR = 0;
private static final int COMPORT_POLL = 1;
private static final int COMPORT_FRONT_PORT_OUT = 2;
private static final int COMPORT_SELF_OUT = 3;
private static final int COMPORT_CONSOLE_CLEAR = 4;
public CubotComPort(Cubot cubot) {
this.cubot = cubot;
public CubotComPort(ControllableUnit unit) {
super(null, unit);
}
public CubotComPort(Document document, ControllableUnit cubot) {
super(document, cubot);
}
private static final int MESSAGE_LENGTH = 8;
@@ -37,21 +38,23 @@ public class CubotComPort extends CpuHardware {
if (a == COMPORT_BUFFER_CLEAR) {
cubot.getConsoleMessagesBuffer().clear();
unit.getConsoleMessagesBuffer().clear();
} else if (a == COMPORT_CONSOLE_CLEAR) {
cubot.setConsoleMode(Cubot.ConsoleMode.CLEAR);
if (unit instanceof Cubot) {
((Cubot) unit).setConsoleMode(Cubot.ConsoleMode.CLEAR);
}
} else if (a == COMPORT_POLL) {
if (cubot.spendEnergy(4)) {
if (unit.spendEnergy(4)) {
int x = getCpu().getRegisterSet().getRegister("X").getValue();
//Read all messages in the console buffer to memory at X
for (char[] message : cubot.getConsoleMessagesBuffer()) {
for (char[] message : unit.getConsoleMessagesBuffer()) {
if (x + MESSAGE_LENGTH >= getCpu().getMemory().getWords().length) {
//todo set interrupt ?
getCpu().getStatus().setErrorFlag(true);
@@ -61,19 +64,19 @@ public class CubotComPort extends CpuHardware {
}
//Set B = number of messages
getCpu().getRegisterSet().getRegister("B").setValue(cubot.getConsoleMessagesBuffer().size());
getCpu().getRegisterSet().getRegister("B").setValue(unit.getConsoleMessagesBuffer().size());
}
} else if (a == COMPORT_FRONT_PORT_OUT) {
if (cubot.spendEnergy(20)) {
if (unit.spendEnergy(5)) {
//Get object directly in front of the Cubot
Point frontTile = cubot.getFrontTile();
Point frontTile = unit.getFrontTile();
//Todo will have to add getGameObjectsBlockingAt to enable Factory
ArrayList<GameObject> objects = cubot.getWorld().getGameObjectsAt(frontTile.x, frontTile.y);
ArrayList<GameObject> objects = unit.getWorld().getGameObjectsAt(frontTile.x, frontTile.y);
if (objects.size() > 0 && objects.get(0) instanceof Programmable) {
if (objects.size() > 0 && objects.get(0) instanceof MessageReceiver) {
int x = getCpu().getRegisterSet().getRegister("X").getValue();
@@ -86,9 +89,9 @@ public class CubotComPort extends CpuHardware {
char[] message = new char[MESSAGE_LENGTH];
System.arraycopy(getCpu().getMemory().getWords(), x, message, 0, MESSAGE_LENGTH);
//Send it to the Programmable object
//Send it to the MessageReceiver object
getCpu().getRegisterSet().getRegister("B").setValue(
((Programmable) objects.get(0)).sendMessage(message) ? 1 : 0);
((MessageReceiver) objects.get(0)).sendMessage(message) ? 1 : 0);
return;
}
}
@@ -98,7 +101,7 @@ public class CubotComPort extends CpuHardware {
} else if (a == COMPORT_SELF_OUT) {
if (cubot.spendEnergy(1)) {
if (unit.spendEnergy(1)) {
int x = getCpu().getRegisterSet().getRegister("X").getValue();
@@ -111,34 +114,17 @@ public class CubotComPort extends CpuHardware {
//Get MESSAGE_LENGTH-word message pointed by X
char[] message = new char[MESSAGE_LENGTH];
System.arraycopy(getCpu().getMemory().getWords(), x, message, 0, MESSAGE_LENGTH);
getCpu().getRegisterSet().getRegister("B").setValue(cubot.sendMessage(message) ? 1 : 0);
getCpu().getRegisterSet().getRegister("B").setValue(unit.sendMessage(message) ? 1 : 0);
return;
}
}
getCpu().getRegisterSet().getRegister("B").setValue(0); //Failed
}
}
@Override
public char getId() {
return HWID;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
public static CubotComPort deserialize(DBObject obj) {
return new CubotComPort((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
}

Some files were not shown because too many files have changed in this diff Show More