mirror of
https://github.com/simon987/Much-Assembly-Required.git
synced 2025-12-14 07:09:04 +00:00
Compare commits
418 Commits
v1.0a
...
refactor-1
| Author | SHA1 | Date | |
|---|---|---|---|
| 18a6858edd | |||
| 9315111793 | |||
| 60c763167d | |||
| 3ae119072c | |||
| 2ace932b52 | |||
| 50b95d1a9c | |||
| ad08a5ac4f | |||
| 84a9132f5e | |||
| 2ff19f24eb | |||
| b1d7121b22 | |||
| 51c0d4c3c7 | |||
| 421a983910 | |||
| 73dc9b1dca | |||
| fe299fe061 | |||
| 319abad130 | |||
| ad0124508c | |||
| ba78d2fd93 | |||
| cc5627902e | |||
| 21a391e077 | |||
| 3b7cff6ac7 | |||
| 304c063887 | |||
| cd9e555e86 | |||
|
|
7c0187f514 | ||
| ac27250f98 | |||
|
|
84f59a5fa2 | ||
| a0193621a8 | |||
| 922162660f | |||
| 014dab49da | |||
|
|
4c81937edc | ||
| 2597b558e6 | |||
| ea1ec1b1b3 | |||
| 8b55466e7c | |||
|
|
03ff2e5527 | ||
|
|
634af8859f | ||
|
|
39ca4d5143 | ||
|
|
5f3c34911c | ||
|
|
638c5b3bb3 | ||
|
|
50e0baa6ec | ||
|
|
3a35f3fdfc | ||
|
|
2982fcbbc1 | ||
|
|
fb97415ee7 | ||
|
|
f37ae36262 | ||
|
|
556b46badc | ||
|
|
d57f8d2970 | ||
|
|
629dac6ea3 | ||
|
|
55fdd93d9d | ||
|
|
2f7d2cd85a | ||
|
|
6e4b2358af | ||
|
|
f9c7f02870 | ||
|
|
86b331fc60 | ||
|
|
35b6f06687 | ||
|
|
bcd96be625 | ||
|
|
f99f327480 | ||
|
|
d71b3dc97c | ||
|
|
e378016c56 | ||
|
|
c50cc8c364 | ||
|
|
bf9cdc7b29 | ||
|
|
75410cc0db | ||
|
|
e0ed1e0e6f | ||
|
|
0fbd1d1a6b | ||
|
|
8018fb3e30 | ||
|
|
fb8fdac9c3 | ||
|
|
a3253e8e3a | ||
|
|
9ee754e5be | ||
|
|
e72e8b45c5 | ||
|
|
311889bc93 | ||
|
|
9cb2c29f0f | ||
|
|
f3ae97c060 | ||
|
|
e8033f3291 | ||
|
|
b19eb0568d | ||
|
|
9aa876df62 | ||
|
|
b1da29d7fb | ||
|
|
12f20d178a | ||
|
|
a030e9fc5d | ||
|
|
439547102f | ||
|
|
d5ddb8e439 | ||
|
|
4f4cecf60c | ||
|
|
a1136f9056 | ||
|
|
d2e7084354 | ||
|
|
1157bd5df5 | ||
|
|
0ff671713c | ||
|
|
2590e8ec64 | ||
|
|
a768cf0f4b | ||
|
|
7b3839f046 | ||
|
|
4958c3d9a0 | ||
|
|
f8eec652ae | ||
|
|
762bad758b | ||
|
|
3e2422775e | ||
|
|
993838651f | ||
|
|
ce8c5375ee | ||
|
|
91ebb51389 | ||
|
|
d113f635a1 | ||
|
|
78a9f78a54 | ||
|
|
4f8eb2725e | ||
|
|
ff4f6c23eb | ||
|
|
c5a34c7a3f | ||
|
|
1d3d441996 | ||
|
|
7ba46ec36e | ||
|
|
63cdfb22ac | ||
|
|
f9898f57f4 | ||
|
|
2548242da8 | ||
|
|
46d5c9b51e | ||
|
|
7fd1c35f7a | ||
|
|
0dbec1d258 | ||
|
|
45d3b84dd0 | ||
|
|
af4ddbdd89 | ||
|
|
84ca3acb52 | ||
|
|
0973548b71 | ||
|
|
5f95c17aed | ||
|
|
b361f87154 | ||
|
|
e4a06e79d4 | ||
|
|
955d61ce99 | ||
|
|
201d83f8d8 | ||
|
|
94b8ef5395 | ||
|
|
70eeb1442d | ||
|
|
bbaa338469 | ||
|
|
5de909cd9c | ||
|
|
71e88afdc9 | ||
|
|
ae41bd9cb9 | ||
|
|
de45eb1827 | ||
|
|
6c7a2f0a73 | ||
|
|
3776070689 | ||
|
|
54ed05b86c | ||
|
|
b361583252 | ||
|
|
04c837d692 | ||
|
|
aaeb18068d | ||
|
|
e848fd8b8a | ||
|
|
1435d31d36 | ||
|
|
950f6b6b4b | ||
|
|
6a9cfb3acb | ||
|
|
4be1bf2e8a | ||
|
|
e50bcdeab7 | ||
|
|
c8214236ab | ||
|
|
37b7bbff98 | ||
|
|
be7304aa0b | ||
|
|
0845438297 | ||
|
|
564a692c2e | ||
|
|
a40a0712f0 | ||
|
|
f89c39c756 | ||
|
|
a6f0ce1dfb | ||
|
|
2f80205b2a | ||
|
|
71f96f27d1 | ||
|
|
98402dd45b | ||
|
|
548d756e90 | ||
|
|
1a2332bc32 | ||
|
|
9a73b7b7d1 | ||
|
|
d860591cc8 | ||
|
|
268acda773 | ||
|
|
6d66e19dc5 | ||
|
|
40e7899cf6 | ||
|
|
abb92bfc75 | ||
|
|
becf6e5feb | ||
|
|
8af652482d | ||
|
|
205845d6af | ||
|
|
64193ecf7a | ||
|
|
a1cf279b6f | ||
|
|
6074238131 | ||
|
|
fa62c49fa8 | ||
|
|
a0aaee7ee7 | ||
|
|
96fc3ed68c | ||
|
|
e69111ff3c | ||
|
|
e7e9a3e6fb | ||
|
|
a73aa7c3a5 | ||
|
|
e8543082ce | ||
|
|
4b7ebd7ad6 | ||
|
|
c389bbc92e | ||
|
|
78f98c8227 | ||
|
|
92008e553a | ||
|
|
45d34c37ad | ||
|
|
a9cc9662f4 | ||
|
|
df9c466827 | ||
|
|
b65c57ceba | ||
|
|
2f74dd45fc | ||
|
|
07160138aa | ||
|
|
b6a206f3c7 | ||
|
|
ed1c4cff0b | ||
|
|
a005e2b163 | ||
|
|
a7bdbd2513 | ||
|
|
ee9eeeef55 | ||
|
|
4b67798180 | ||
|
|
be8dd14d36 | ||
|
|
80f45f1eb0 | ||
|
|
3368268924 | ||
|
|
4cd58c86a5 | ||
|
|
8d029cf621 | ||
|
|
10f088cb66 | ||
|
|
159c217d59 | ||
|
|
c5cb5df335 | ||
|
|
854863ede9 | ||
|
|
e98575b23f | ||
|
|
1a5d12a19f | ||
|
|
1678be25c5 | ||
|
|
9c41c16079 | ||
|
|
e97ecbe380 | ||
|
|
083af31b84 | ||
|
|
2c856aae80 | ||
|
|
315e33055e | ||
|
|
e025b6d2da | ||
|
|
e3a650a4fc | ||
|
|
113aa50d87 | ||
|
|
dc034d1437 | ||
|
|
3492e133e1 | ||
|
|
bd5f8573e8 | ||
|
|
3c5bfdb30b | ||
|
|
9349ae108b | ||
|
|
292adb5483 | ||
|
|
e479d89375 | ||
|
|
41d674d74a | ||
|
|
227f7ce5aa | ||
|
|
a091071d55 | ||
|
|
f1b8f3dc6d | ||
|
|
e62a51ee2e | ||
|
|
217c997788 | ||
|
|
ff61433c4b | ||
|
|
cbde2450fa | ||
|
|
1d780f7d9b | ||
|
|
cbb07891fc | ||
|
|
2565d3338c | ||
|
|
e4269b83c4 | ||
|
|
9cac665101 | ||
|
|
dc19176dc8 | ||
|
|
8ed192f8d0 | ||
|
|
d1a3cf9307 | ||
|
|
f8c5dac969 | ||
|
|
0a75cb557d | ||
|
|
f35e6c5a9a | ||
|
|
0ada6c29d4 | ||
|
|
156deb8f4e | ||
|
|
039088ac00 | ||
|
|
f530dafdee | ||
|
|
817dbcc6c4 | ||
|
|
62f1403cb3 | ||
|
|
8c6e580ea9 | ||
|
|
6a1519d97d | ||
|
|
a7d1a00ae8 | ||
|
|
95a14ad1ab | ||
|
|
8d961ce572 | ||
|
|
c772abe0bf | ||
|
|
187a828c79 | ||
|
|
ffca185fe5 | ||
|
|
815f3de234 | ||
|
|
3505a466bb | ||
|
|
3d10e4306b | ||
|
|
210e579995 | ||
|
|
a285b3104e | ||
|
|
947deea784 | ||
|
|
4293fc0315 | ||
|
|
33955d9639 | ||
|
|
6b91251b4e | ||
|
|
695341428a | ||
|
|
c610929809 | ||
|
|
54b72e89b3 | ||
|
|
59fd620e4a | ||
|
|
2fbc55d0dd | ||
|
|
e1dfb08635 | ||
|
|
f1c1f8f807 | ||
|
|
7cbfb822b8 | ||
|
|
a04207b5e0 | ||
|
|
6fc583d6f0 | ||
|
|
46483b2bf8 | ||
|
|
d65660b5e9 | ||
|
|
593be7624e | ||
|
|
a14853b12c | ||
|
|
735a231767 | ||
|
|
1131e4d512 | ||
|
|
cfb02869bb | ||
|
|
cde03af8af | ||
|
|
ff9370fed5 | ||
|
|
eaef30eb49 | ||
|
|
f68027bb4d | ||
|
|
24d81d194e | ||
|
|
c7b9df0690 | ||
|
|
a8beb8c3e2 | ||
|
|
0bd25df977 | ||
|
|
9bb0dc9034 | ||
|
|
8b0460f5f1 | ||
|
|
ce0584a49f | ||
|
|
0dc4ddca0f | ||
|
|
1da894959e | ||
|
|
7e773699b6 | ||
|
|
bc29c36204 | ||
|
|
811a443a4e | ||
|
|
17359161fd | ||
|
|
afbbce215e | ||
|
|
1a39cf8454 | ||
|
|
9bc3cbf4ce | ||
|
|
9b908a5310 | ||
|
|
8ceeca564a | ||
|
|
d4fcf22550 | ||
|
|
d25d42534a | ||
|
|
58a378d77f | ||
|
|
cfaf46ad52 | ||
|
|
eb3deba544 | ||
|
|
ba53212986 | ||
|
|
7bb5f68ff2 | ||
|
|
65ec6a2102 | ||
|
|
5a994fe437 | ||
|
|
415500faa9 | ||
|
|
3ded64cb16 | ||
|
|
1a0291d517 | ||
|
|
4e76d57ef9 | ||
|
|
c7da764505 | ||
|
|
2a5d6b5a13 | ||
|
|
e2763faeee | ||
|
|
2a8658c598 | ||
|
|
85548ec3cb | ||
|
|
0f7b40f3ae | ||
|
|
ee60216784 | ||
|
|
c3a0d1bd4d | ||
|
|
cd8d883134 | ||
|
|
c7946a6356 | ||
|
|
7ee361b0bc | ||
|
|
1e26c63358 | ||
|
|
d832f65535 | ||
|
|
e47e573b61 | ||
|
|
93786d92cb | ||
|
|
34178016b1 | ||
|
|
b31c187ad5 | ||
|
|
deb3859dff | ||
|
|
b21e33601e | ||
|
|
b2474494cc | ||
|
|
6b2e3c17ed | ||
|
|
079a63e39c | ||
|
|
347dc2bce0 | ||
|
|
90ad8e82eb | ||
|
|
04374e6e0a | ||
|
|
4a6ee4cce6 | ||
|
|
37e40d0329 | ||
|
|
99cefba245 | ||
|
|
7821d428e2 | ||
|
|
93bea49a3e | ||
|
|
cca68ba139 | ||
|
|
83f05efff6 | ||
|
|
ea3b06c54c | ||
|
|
1ed9e9e4db | ||
|
|
08f1aa8686 | ||
|
|
55d4c19fe1 | ||
|
|
2398219fab | ||
|
|
81767ed5cf | ||
|
|
2ef6f492c4 | ||
|
|
70a55dce59 | ||
|
|
fe0be03ab8 | ||
|
|
fd73a47796 | ||
|
|
eea9420192 | ||
|
|
9e402fe8a1 | ||
|
|
b3d88b2813 | ||
|
|
ef7f573256 | ||
|
|
5afa767b4a | ||
|
|
a9e580f2ce | ||
|
|
c4caf4ab0b | ||
|
|
0c06d0bf09 | ||
|
|
017b89f97f | ||
|
|
bd276e0a93 | ||
|
|
72d43fccf7 | ||
|
|
18aaf91991 | ||
|
|
bbcadbf253 | ||
|
|
3d60f9a67c | ||
|
|
b43cf9ac33 | ||
|
|
293795b215 | ||
|
|
fde79ed97e | ||
|
|
fcd339c315 | ||
|
|
59e3e9430e | ||
|
|
45ec7191b4 | ||
|
|
d9732557fc | ||
|
|
eced45a358 | ||
|
|
0fee35baec | ||
|
|
729debb1a3 | ||
|
|
2fb7d2f73e | ||
|
|
dab5cab602 | ||
|
|
c23ee0dc86 | ||
|
|
96cce8478e | ||
|
|
9d184cb515 | ||
|
|
09e00fd032 | ||
|
|
e1d0833c3a | ||
|
|
8a980fec6a | ||
|
|
e96b7982c8 | ||
|
|
21ec4ae704 | ||
|
|
690b7e8d31 | ||
|
|
d28ca387d0 | ||
|
|
f2b6387cc9 | ||
|
|
f3b20b3a2d | ||
|
|
f4fd3866eb | ||
|
|
965ca91cb6 | ||
|
|
556f443ec2 | ||
|
|
3158dd75ea | ||
|
|
1b6927c575 | ||
|
|
3bd34bfcba | ||
|
|
2e9248ea2e | ||
|
|
4f1342593f | ||
|
|
3548928218 | ||
|
|
cd41db9e58 | ||
|
|
597118bd07 | ||
|
|
9dd9b45d2d | ||
|
|
9597a80edf | ||
|
|
d24363fd82 | ||
|
|
be45979ed0 | ||
|
|
29cac77e79 | ||
|
|
6be2a496c6 | ||
|
|
12db25e726 | ||
|
|
d004386b7b | ||
|
|
e09d2c1b16 | ||
|
|
c703dec3cf | ||
|
|
8701007ad9 | ||
|
|
cfb8050cee | ||
|
|
da7d050661 | ||
|
|
3ee9b4be95 | ||
|
|
42421b7710 | ||
|
|
9979986c6a | ||
|
|
c5abe2efc5 | ||
|
|
fdfa568e07 | ||
|
|
1bf1682cb3 | ||
|
|
a3fa3c4c09 | ||
|
|
f7ea14275c | ||
|
|
04d51e1f1f | ||
|
|
8c5dcd0fba | ||
|
|
1a6f92a29d | ||
|
|
3d9bfb2a42 | ||
|
|
e2ed744479 |
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal 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
|
||||||
18
.gitignore
vendored
Normal file
18
.gitignore
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
.idea/*
|
||||||
|
mar.log
|
||||||
|
mar.log.lck
|
||||||
|
*.iml
|
||||||
|
*.class
|
||||||
|
*/target/*
|
||||||
|
plugins/*.jar
|
||||||
|
save.json
|
||||||
|
Server/Server.iml
|
||||||
|
target/*
|
||||||
|
src/main/java/META-INF/MANIFEST.MF
|
||||||
|
src/main/resources/static/js/mar.js
|
||||||
|
.settings
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
# VSCode Workspace
|
||||||
|
.vscode/
|
||||||
14
CONTRIBUTING.md
Normal file
14
CONTRIBUTING.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
## General guide
|
||||||
|
|
||||||
|
[Collaboration guide](https://github.com/simon987/Much-Assembly-Required/wiki/Collaboration-Guide)
|
||||||
|
|
||||||
|
## Before creating a pull request
|
||||||
|
|
||||||
|
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
|
||||||
|
- 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.
|
||||||
|
- Please state what tests have been performed in the pull request
|
||||||
6
Dockerfile
Normal file
6
Dockerfile
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
FROM maven:3.5-jdk-8
|
||||||
|
COPY /. /app/
|
||||||
|
WORKDIR /app
|
||||||
|
RUN mvn package
|
||||||
|
WORKDIR /app/target
|
||||||
|
CMD ["java", "-jar", "/app/target/server-1.6a.jar"]
|
||||||
674
LICENSE
Normal file
674
LICENSE
Normal file
@@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the program's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) <year> <name of author>
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
classpath=net.simon987.cubotplugin.CubotPlugin
|
|
||||||
name=Cubot Plugin
|
|
||||||
version=1.0
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
package net.simon987.cubotplugin;
|
|
||||||
|
|
||||||
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 {
|
|
||||||
|
|
||||||
private static final char MAP_INFO = 0x0080;
|
|
||||||
public static final int ID = 1;
|
|
||||||
|
|
||||||
private EffectType currentEmote = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hit points
|
|
||||||
*/
|
|
||||||
private int hp;
|
|
||||||
private int heldItem;
|
|
||||||
|
|
||||||
private CubotAction currentAction = CubotAction.IDLE;
|
|
||||||
private CubotAction lastAction = CubotAction.IDLE;
|
|
||||||
|
|
||||||
private ArrayList<Integer> keyboardBuffer = new ArrayList<>();
|
|
||||||
|
|
||||||
private User parent;
|
|
||||||
|
|
||||||
public Cubot() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char getMapInfo() {
|
|
||||||
return MAP_INFO;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update() {
|
|
||||||
|
|
||||||
if (currentAction == CubotAction.WALKING) {
|
|
||||||
if (!incrementLocation()) {
|
|
||||||
//Couldn't walk
|
|
||||||
currentAction = CubotAction.IDLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentEmote != null) {
|
|
||||||
// getWorld().getQueuedGameEffects().add(new GameEffect(currentEmote, getX(), getY()));
|
|
||||||
currentEmote = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 = CubotAction.IDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject serialise() {
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
json.put("id", getObjectId());
|
|
||||||
json.put("type", 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());
|
|
||||||
if (parent != null) {
|
|
||||||
json.put("parent", parent.getUsername()); //Only used client-side for now
|
|
||||||
}
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Cubot deserialize(JSONObject json) {
|
|
||||||
|
|
||||||
Cubot cubot = new Cubot();
|
|
||||||
cubot.setObjectId((int)(long)json.get("id"));
|
|
||||||
cubot.setX((int)(long)json.get("x"));
|
|
||||||
cubot.setY((int)(long)json.get("y"));
|
|
||||||
cubot.hp = (int)(long)json.get("hp");
|
|
||||||
cubot.setDirection(Direction.getDirection((int)(long)json.get("direction")));
|
|
||||||
cubot.heldItem = (int)(long)json.get("heldItem");
|
|
||||||
|
|
||||||
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(CubotAction currentAction) {
|
|
||||||
this.currentAction = currentAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
public User getParent() {
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setParent(User parent) {
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CubotAction getAction() {
|
|
||||||
return lastAction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package net.simon987.cubotplugin;
|
|
||||||
|
|
||||||
public enum CubotAction {
|
|
||||||
IDLE,
|
|
||||||
DIGGING,
|
|
||||||
WALKING,
|
|
||||||
WITHDRAWING,
|
|
||||||
DEPOSITING
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
package net.simon987.cubotplugin;
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.assembly.CpuHardware;
|
|
||||||
import net.simon987.server.assembly.Status;
|
|
||||||
import net.simon987.server.game.TileMap;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
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 POLL = 1;
|
|
||||||
private static final int GATHER_SLOW = 2;
|
|
||||||
private static final int GATHER_FAST = 3;
|
|
||||||
|
|
||||||
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 == POLL) {
|
|
||||||
|
|
||||||
getCpu().getRegisterSet().getRegister("B").setValue(0);
|
|
||||||
|
|
||||||
} else if (a == GATHER_SLOW || a == GATHER_FAST) {
|
|
||||||
|
|
||||||
if (cubot.getAction() != CubotAction.IDLE) {
|
|
||||||
int tile = cubot.getWorld().getTileMap().getTileAt(cubot.getX(), cubot.getY());
|
|
||||||
|
|
||||||
if (tile == TileMap.IRON_TILE) {
|
|
||||||
cubot.setHeldItem(TileMap.ITEM_IRON);
|
|
||||||
cubot.setCurrentAction(CubotAction.DIGGING);
|
|
||||||
|
|
||||||
} else if (tile == TileMap.COPPER_TILE) {
|
|
||||||
cubot.setHeldItem(TileMap.ITEM_COPPER);
|
|
||||||
cubot.setCurrentAction(CubotAction.DIGGING);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//System.out.println("FAILED: dig");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject serialise() {
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
json.put("hwid", (int) HWID);
|
|
||||||
json.put("cubot", cubot.getObjectId());
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CubotDrill deserialize(JSONObject hwJSON){
|
|
||||||
return new CubotDrill((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((int)(long)hwJSON.get("cubot")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package net.simon987.cubotplugin;
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.assembly.CpuHardware;
|
|
||||||
import net.simon987.server.assembly.Status;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
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 POLL = 1;
|
|
||||||
private static final int CLEAR = 2;
|
|
||||||
|
|
||||||
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 == POLL) {
|
|
||||||
|
|
||||||
getCpu().getRegisterSet().getRegister("B").setValue(cubot.getHeldItem());
|
|
||||||
|
|
||||||
} else if (a == CLEAR) {
|
|
||||||
cubot.setHeldItem(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject serialise() {
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
json.put("hwid", (int) HWID);
|
|
||||||
json.put("cubot", cubot.getObjectId());
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CubotInventory deserialize(JSONObject hwJSON){
|
|
||||||
return new CubotInventory((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((int)(long)hwJSON.get("cubot")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package net.simon987.cubotplugin;
|
|
||||||
|
|
||||||
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.InventoryHolder;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
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 WITHDRAW = 1;
|
|
||||||
|
|
||||||
|
|
||||||
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 == WITHDRAW) {
|
|
||||||
|
|
||||||
//System.out.println("withdraw");
|
|
||||||
|
|
||||||
Point frontTile = cubot.getFrontTile();
|
|
||||||
ArrayList<GameObject> objects = cubot.getWorld().getGameObjectsAt(frontTile.x, frontTile.y);
|
|
||||||
|
|
||||||
|
|
||||||
if (cubot.getAction() != CubotAction.IDLE && objects.size() > 0) {
|
|
||||||
|
|
||||||
if (objects.get(0) instanceof InventoryHolder) {
|
|
||||||
//Take the item
|
|
||||||
if (((InventoryHolder) objects.get(0)).takeItem(b)) {
|
|
||||||
|
|
||||||
cubot.setHeldItem(b);
|
|
||||||
//System.out.println("took " + b);
|
|
||||||
cubot.setCurrentAction(CubotAction.WITHDRAWING);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//The inventory holder can't provide this item
|
|
||||||
//todo Add emote here
|
|
||||||
// System.out.println("DEBUG: FAILED: take (The inventory holder can't provide this item)");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
//Nothing in front
|
|
||||||
// System.out.println("DEBUG: FAILED: take (Nothing in front or Cubot is busy)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject serialise() {
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
json.put("hwid", (int) HWID);
|
|
||||||
json.put("cubot", cubot.getObjectId());
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CubotLaser deserialize(JSONObject hwJSON){
|
|
||||||
return new CubotLaser((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((int)(long)hwJSON.get("cubot")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
package net.simon987.cubotplugin;
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.assembly.CpuHardware;
|
|
||||||
import net.simon987.server.assembly.Status;
|
|
||||||
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 SET_DIR = 1;
|
|
||||||
private static final int 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) {
|
|
||||||
int a = getCpu().getRegisterSet().getRegister("A").getValue();
|
|
||||||
int b = getCpu().getRegisterSet().getRegister("B").getValue();
|
|
||||||
|
|
||||||
if(a == SET_DIR){
|
|
||||||
|
|
||||||
Direction dir = Direction.getDirection(b);
|
|
||||||
|
|
||||||
if(dir != null){
|
|
||||||
cubot.setDirection(Direction.getDirection(b));
|
|
||||||
status.setErrorFlag(false);
|
|
||||||
} else {
|
|
||||||
status.setErrorFlag(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} else if(a == SET_DIR_AND_WALK){
|
|
||||||
|
|
||||||
Direction dir = Direction.getDirection(b);
|
|
||||||
|
|
||||||
if(dir != null){
|
|
||||||
cubot.setDirection(Direction.getDirection(b));
|
|
||||||
status.setErrorFlag(false);
|
|
||||||
} else {
|
|
||||||
status.setErrorFlag(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
cubot.setCurrentAction(CubotAction.WALKING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject serialise() {
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
json.put("hwid", (int) HWID);
|
|
||||||
json.put("cubot", cubot.getObjectId());
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CubotLeg deserialize(JSONObject hwJSON){
|
|
||||||
return new CubotLeg((Cubot)GameServer.INSTANCE.getGameUniverse().getObject((int)(long)hwJSON.get("cubot")));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
package net.simon987.cubotplugin;
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.assembly.CpuHardware;
|
|
||||||
import net.simon987.server.assembly.Status;
|
|
||||||
import net.simon987.server.game.World;
|
|
||||||
import net.simon987.server.game.pathfinding.Node;
|
|
||||||
import net.simon987.server.game.pathfinding.Pathfinder;
|
|
||||||
import net.simon987.server.io.JSONSerialisable;
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class CubotLidar extends CpuHardware implements JSONSerialisable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hardware ID (Should be unique)
|
|
||||||
*/
|
|
||||||
public static final char HWID = 0x0003;
|
|
||||||
|
|
||||||
public static final int DEFAULT_ADDRESS = 3;
|
|
||||||
|
|
||||||
private Cubot cubot;
|
|
||||||
|
|
||||||
private static final int GET_POS = 1;
|
|
||||||
private static final int GET_PATH = 2;
|
|
||||||
private static final int GET_MAP = 3;
|
|
||||||
|
|
||||||
private static final int MEMORY_MAP_START = 0x0100;
|
|
||||||
private static final int MEMORY_PATH_START = 0x0000;
|
|
||||||
|
|
||||||
public CubotLidar(Cubot cubot) {
|
|
||||||
this.cubot = cubot;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char getId() {
|
|
||||||
return HWID;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handleInterrupt(Status status) {
|
|
||||||
|
|
||||||
int a = getCpu().getRegisterSet().getRegister("A").getValue();
|
|
||||||
|
|
||||||
switch (a){
|
|
||||||
case GET_POS:
|
|
||||||
getCpu().getRegisterSet().getRegister("X").setValue(cubot.getX());
|
|
||||||
getCpu().getRegisterSet().getRegister("Y").setValue(cubot.getY());
|
|
||||||
break;
|
|
||||||
case GET_PATH:
|
|
||||||
int b = getCpu().getRegisterSet().getRegister("B").getValue();
|
|
||||||
int destX = getCpu().getRegisterSet().getRegister("X").getValue();
|
|
||||||
int destY = getCpu().getRegisterSet().getRegister("Y").getValue();
|
|
||||||
|
|
||||||
//Get path
|
|
||||||
ArrayList<Node> nodes = Pathfinder.findPath(cubot.getWorld(), cubot.getX(), cubot.getY(),
|
|
||||||
destX, destY, b);
|
|
||||||
|
|
||||||
// System.out.println(nodes.size() + " nodes");
|
|
||||||
|
|
||||||
//Write to memory
|
|
||||||
byte[] mem = getCpu().getMemory().getBytes();
|
|
||||||
|
|
||||||
int counter = 0; //todo get memory address from config/constant
|
|
||||||
|
|
||||||
if (nodes != null) {
|
|
||||||
|
|
||||||
Node lastNode = null;
|
|
||||||
|
|
||||||
for (Node n : nodes) {
|
|
||||||
//Store the path as a sequence of directions
|
|
||||||
|
|
||||||
if (lastNode == null) {
|
|
||||||
lastNode = n;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n.x < lastNode.x) {
|
|
||||||
//West
|
|
||||||
mem[counter++] = 0;
|
|
||||||
mem[counter++] = 3;
|
|
||||||
} else if (n.x > lastNode.x) {
|
|
||||||
//East
|
|
||||||
mem[counter++] = 0;
|
|
||||||
mem[counter++] = 1;
|
|
||||||
} else if (n.y < lastNode.y) {
|
|
||||||
//North
|
|
||||||
mem[counter++] = 0;
|
|
||||||
mem[counter++] = 0;
|
|
||||||
} else if (n.y > lastNode.y) {
|
|
||||||
//South
|
|
||||||
mem[counter++] = 0;
|
|
||||||
mem[counter++] = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastNode = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Indicate end of path with 0xAAAA
|
|
||||||
mem[counter++] = -86;
|
|
||||||
mem[counter] = -86;
|
|
||||||
} else {
|
|
||||||
//Indicate invalid path 0xFFFF
|
|
||||||
mem[counter++] = -1;
|
|
||||||
mem[counter] = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogManager.LOGGER.fine("DEBUG: path to" + destX + "," + destY);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GET_MAP:
|
|
||||||
char[][] mapInfo = cubot.getWorld().getMapInfo();
|
|
||||||
|
|
||||||
int i = MEMORY_MAP_START;
|
|
||||||
for (int y = 0; y < World.WORLD_SIZE; y++) {
|
|
||||||
for (int x = 0; x < World.WORLD_SIZE; x++) {
|
|
||||||
getCpu().getMemory().set(i++, mapInfo[x][y]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject serialise() {
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
json.put("hwid", (int) HWID);
|
|
||||||
json.put("cubot", cubot.getObjectId());
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CubotLidar deserialize(JSONObject hwJSON) {
|
|
||||||
return new CubotLidar((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((int) (long) hwJSON.get("cubot")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
package net.simon987.cubotplugin;
|
|
||||||
|
|
||||||
import net.simon987.cubotplugin.event.CpuInitialisationListener;
|
|
||||||
import net.simon987.cubotplugin.event.UserCreationListener;
|
|
||||||
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 org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public class CubotPlugin extends ServerPlugin implements GameObjectDeserializer, CpuHardwareDeserializer{
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init() {
|
|
||||||
listeners.add(new CpuInitialisationListener());
|
|
||||||
listeners.add(new UserCreationListener());
|
|
||||||
|
|
||||||
LogManager.LOGGER.info("Initialised Cubot plugin");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GameObject deserializeObject(JSONObject object) {
|
|
||||||
|
|
||||||
int objType = (int)(long)object.get("type");
|
|
||||||
|
|
||||||
if(objType == Cubot.ID) {
|
|
||||||
|
|
||||||
return Cubot.deserialize(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CpuHardware deserializeHardware(JSONObject hwJson) {
|
|
||||||
int hwid = (int)(long)hwJson.get("hwid");
|
|
||||||
|
|
||||||
switch (hwid){
|
|
||||||
case CubotLeg.HWID:
|
|
||||||
return CubotLeg.deserialize(hwJson);
|
|
||||||
case CubotLaser.HWID:
|
|
||||||
return CubotLaser.deserialize(hwJson);
|
|
||||||
case CubotLidar.HWID:
|
|
||||||
return CubotLidar.deserialize(hwJson);
|
|
||||||
case CubotDrill.HWID:
|
|
||||||
return CubotDrill.deserialize(hwJson);
|
|
||||||
case CubotInventory.HWID:
|
|
||||||
return CubotInventory.deserialize(hwJson);
|
|
||||||
case Keyboard.HWID:
|
|
||||||
return Keyboard.deserialize(hwJson);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package net.simon987.cubotplugin;
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.assembly.CpuHardware;
|
|
||||||
import net.simon987.server.assembly.Status;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public class Keyboard extends CpuHardware {
|
|
||||||
|
|
||||||
public static final int DEFAULT_ADDRESS = 4;
|
|
||||||
|
|
||||||
public static final String NAME = "Wireless Keyboard";
|
|
||||||
|
|
||||||
|
|
||||||
private static final int CLEAR_BUFFER = 0;
|
|
||||||
private static final int FETCH_KEY = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hardware ID (Should be unique)
|
|
||||||
*/
|
|
||||||
public static final char HWID = 0x0004;
|
|
||||||
|
|
||||||
private Cubot cubot;
|
|
||||||
|
|
||||||
public Keyboard(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 == CLEAR_BUFFER){
|
|
||||||
|
|
||||||
cubot.clearKeyboardBuffer();
|
|
||||||
|
|
||||||
} else if (a == FETCH_KEY){
|
|
||||||
//pop
|
|
||||||
int key = 0;
|
|
||||||
if(cubot.getKeyboardBuffer().size() > 0){
|
|
||||||
key = cubot.getKeyboardBuffer().get(0);
|
|
||||||
cubot.getKeyboardBuffer().remove(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCpu().getRegisterSet().getRegister("B").setValue(key);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject serialise() {
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
json.put("hwid", (int) HWID);
|
|
||||||
json.put("cubot", cubot.getObjectId());
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Keyboard deserialize(JSONObject hwJSON){
|
|
||||||
return new Keyboard((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((int)(long)hwJSON.get("cubot")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +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.logging.LogManager;
|
|
||||||
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);
|
|
||||||
Keyboard keyboard = new Keyboard((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);
|
|
||||||
|
|
||||||
cpu.attachHardware(legHw, CubotLeg.DEFAULT_ADDRESS);
|
|
||||||
cpu.attachHardware(laserHw, CubotLaser.DEFAULT_ADDRESS);
|
|
||||||
cpu.attachHardware(radarHw, CubotLidar.DEFAULT_ADDRESS);
|
|
||||||
cpu.attachHardware(keyboard, Keyboard.DEFAULT_ADDRESS);
|
|
||||||
cpu.attachHardware(drillHw, CubotDrill.DEFAULT_ADDRESS);
|
|
||||||
cpu.attachHardware(invHw, CubotInventory.DEFAULT_ADDRESS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,47 +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.*;
|
|
||||||
|
|
||||||
public class UserCreationListener implements GameEventListener {
|
|
||||||
@Override
|
|
||||||
public Class getListenedEventType() {
|
|
||||||
return UserCreationEvent.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(GameEvent event) {
|
|
||||||
|
|
||||||
User user = (User)event.getSource();
|
|
||||||
|
|
||||||
LogManager.LOGGER.fine("(Plugin) Handled User creation event (Cubot Plugin)");
|
|
||||||
|
|
||||||
Cubot cubot = new Cubot();
|
|
||||||
|
|
||||||
cubot.setWorld(GameServer.INSTANCE.getGameUniverse().getWorld(
|
|
||||||
GameServer.INSTANCE.getConfig().getInt("new_user_worldX"),
|
|
||||||
GameServer.INSTANCE.getConfig().getInt("new_user_worldY")));
|
|
||||||
cubot.getWorld().getGameObjects().add(cubot);
|
|
||||||
|
|
||||||
cubot.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
|
|
||||||
|
|
||||||
cubot.setHeldItem(GameServer.INSTANCE.getConfig().getInt("new_user_item"));
|
|
||||||
|
|
||||||
cubot.setParent(user);
|
|
||||||
|
|
||||||
Point point = cubot.getWorld().getRandomPassableTile();
|
|
||||||
|
|
||||||
cubot.setX(point.x);
|
|
||||||
cubot.setY(point.y);
|
|
||||||
|
|
||||||
user.setControlledUnit(cubot);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
classpath=net.simon987.kilnplugin.KilnPlugin
|
|
||||||
name=Kiln Plugin
|
|
||||||
version=1.0
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package net.simon987.kilnplugin;
|
|
||||||
|
|
||||||
import net.simon987.server.game.GameObject;
|
|
||||||
import net.simon987.server.io.GameObjectDeserializer;
|
|
||||||
import net.simon987.server.plugin.ServerPlugin;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public class KilnPlugin extends ServerPlugin implements GameObjectDeserializer {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GameObject deserializeObject(JSONObject object) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
classpath=net.simon987.plantplugin.PlantPlugin
|
|
||||||
name=Plant Plugin
|
|
||||||
version=1.0
|
|
||||||
@@ -1,162 +0,0 @@
|
|||||||
package net.simon987.plantplugin;
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.game.GameObject;
|
|
||||||
import net.simon987.server.game.InventoryHolder;
|
|
||||||
import net.simon987.server.game.Updatable;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public class Plant extends GameObject implements Updatable, InventoryHolder{
|
|
||||||
|
|
||||||
private static final char MAP_INFO = 0x4000;
|
|
||||||
public static final int ID = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Grow time (see config.properties)
|
|
||||||
*/
|
|
||||||
private static final int GROW_TIME = GameServer.INSTANCE.getConfig().getInt("plant_grow_time");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Game time of the creation of this Plant
|
|
||||||
*/
|
|
||||||
private long creationTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether the plant is grown or not
|
|
||||||
*/
|
|
||||||
private boolean grown;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Yield of the plant, in biomass units
|
|
||||||
*/
|
|
||||||
private int biomassCount;
|
|
||||||
/**
|
|
||||||
* Style of the plant (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("type", ID);
|
|
||||||
json.put("id", getObjectId());
|
|
||||||
json.put("x", getX());
|
|
||||||
json.put("y", getY());
|
|
||||||
json.put("creationTime", creationTime);
|
|
||||||
json.put("grown", grown);
|
|
||||||
json.put("biomassCount", biomassCount);
|
|
||||||
json.put("style", style);
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called every tick
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void update() {
|
|
||||||
if (!grown) {
|
|
||||||
//Check grow
|
|
||||||
if (creationTime + GROW_TIME <= GameServer.INSTANCE.getGameUniverse().getTime()) {
|
|
||||||
grown = true;
|
|
||||||
biomassCount = GameServer.INSTANCE.getConfig().getInt("plant_yield");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getCreationTime() {
|
|
||||||
return creationTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCreationTime(long creationTime) {
|
|
||||||
this.creationTime = creationTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGrown() {
|
|
||||||
return grown;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGrown(boolean grown) {
|
|
||||||
this.grown = grown;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 Plant deserialize(JSONObject json){
|
|
||||||
|
|
||||||
Plant plant = new Plant();
|
|
||||||
|
|
||||||
plant.setObjectId((int)(long)json.get("id"));
|
|
||||||
plant.setX((int)(long)json.get("x"));
|
|
||||||
plant.setY((int)(long)json.get("y"));
|
|
||||||
plant.grown = (boolean)json.get("grown");
|
|
||||||
plant.creationTime = (long)json.get("creationTime");
|
|
||||||
plant.style = (int)(long)json.get("style");
|
|
||||||
plant.biomassCount = (int)(long)json.get("biomassCount");
|
|
||||||
|
|
||||||
return plant;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when an object attempts to place an item in this Plant
|
|
||||||
*
|
|
||||||
* @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 plant?
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when an object attempts to take an item from this Plant.
|
|
||||||
* 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_*)
|
|
||||||
* @return true if the requested item is ITEM_BIOMASS and if the plant is grown
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean takeItem(int item) {
|
|
||||||
|
|
||||||
if (item == ITM_BIOMASS) {
|
|
||||||
if (grown && biomassCount > 1) {
|
|
||||||
biomassCount--;
|
|
||||||
return true;
|
|
||||||
} else if (grown) {
|
|
||||||
//Delete plant
|
|
||||||
setDead(true);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package net.simon987.plantplugin;
|
|
||||||
|
|
||||||
import net.simon987.plantplugin.event.WorldCreationListener;
|
|
||||||
import net.simon987.server.game.GameObject;
|
|
||||||
import net.simon987.server.io.GameObjectDeserializer;
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import net.simon987.server.plugin.ServerPlugin;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public class PlantPlugin extends ServerPlugin implements GameObjectDeserializer {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void init() {
|
|
||||||
listeners.add(new WorldCreationListener());
|
|
||||||
LogManager.LOGGER.info("Initialised Plant plugin");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GameObject deserializeObject(JSONObject object) {
|
|
||||||
|
|
||||||
int objType = (int)(long)object.get("type");
|
|
||||||
|
|
||||||
if(objType == Plant.ID) {
|
|
||||||
|
|
||||||
return Plant.deserialize(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
package net.simon987.plantplugin.event;
|
|
||||||
|
|
||||||
import net.simon987.plantplugin.Plant;
|
|
||||||
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.ArrayList;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class WorldCreationListener implements GameEventListener {
|
|
||||||
@Override
|
|
||||||
public Class getListenedEventType() {
|
|
||||||
return WorldGenerationEvent.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(GameEvent event) {
|
|
||||||
|
|
||||||
ArrayList<Plant> plants = generatePlants(((WorldGenerationEvent)event).getWorld());
|
|
||||||
|
|
||||||
((WorldGenerationEvent)event).getWorld().getGameObjects().addAll(plants);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a list of plants for a world
|
|
||||||
*/
|
|
||||||
public ArrayList<Plant> generatePlants(World world) {
|
|
||||||
|
|
||||||
int minTreeCount = GameServer.INSTANCE.getConfig().getInt("minTreeCount");
|
|
||||||
int maxTreeCount = GameServer.INSTANCE.getConfig().getInt("maxTreeCount");
|
|
||||||
int plant_yield = GameServer.INSTANCE.getConfig().getInt("plant_yield");
|
|
||||||
|
|
||||||
Random random = new Random();
|
|
||||||
int treeCount = random.nextInt(maxTreeCount - minTreeCount) + minTreeCount;
|
|
||||||
ArrayList<Plant> plants = new ArrayList<>(maxTreeCount);
|
|
||||||
|
|
||||||
//Count number of plain tiles. If there is less plain tiles than desired amount of trees,
|
|
||||||
//set the desired amount of trees to the plain tile count
|
|
||||||
int[][] tiles = world.getTileMap().getTiles();
|
|
||||||
int plainCount = 0;
|
|
||||||
for (int y = 0; y < World.WORLD_SIZE; y++) {
|
|
||||||
for (int x = 0; x < World.WORLD_SIZE; x++) {
|
|
||||||
|
|
||||||
if (tiles[x][y] == 0) {
|
|
||||||
plainCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (treeCount > plainCount) {
|
|
||||||
treeCount = plainCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
outerLoop:
|
|
||||||
for (int i = 0; i < treeCount; i++) {
|
|
||||||
|
|
||||||
Point p = world.getTileMap().getRandomPlainTile();
|
|
||||||
if (p != null) {
|
|
||||||
|
|
||||||
//Don't block worlds
|
|
||||||
while (p.x == 0 || p.y == 0 || p.x == World.WORLD_SIZE - 1 || p.y == World.WORLD_SIZE - 1) {
|
|
||||||
p = world.getTileMap().getRandomPlainTile();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Plant plant : plants) {
|
|
||||||
if (plant.getX() == p.x && plant.getY() == p.y) {
|
|
||||||
//There is already a plant here
|
|
||||||
continue outerLoop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Plant plant = new Plant();
|
|
||||||
plant.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
|
|
||||||
plant.setStyle(0); //TODO: set style depending on difficulty level? or random? from config?
|
|
||||||
plant.setBiomassCount(plant_yield);
|
|
||||||
plant.setCreationTime(0); // Plants generated by the world generator always have creationTime of 0
|
|
||||||
plant.setX(p.x);
|
|
||||||
plant.setY(p.y);
|
|
||||||
plant.setWorld(world);
|
|
||||||
|
|
||||||
plants.add(plant);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LogManager.LOGGER.info("Generated " + plants.size() + " plants for World (" + world.getX() + ',' +
|
|
||||||
world.getY() + ')');
|
|
||||||
|
|
||||||
return plants;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
167
README.md
167
README.md
@@ -1,3 +1,166 @@
|
|||||||
# Much-Assembly-Required
|
### [Official website](https://muchassemblyrequired.com)
|
||||||
|
|
||||||
https://muchassemblyrequired.com
|
[](https://www.codefactor.io/repository/github/simon987/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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Wiki: [GitHub](https://github.com/simon987/Much-Assembly-Required/wiki)
|
||||||
|
Chat: [Slack](https://join.slack.com/t/muchassemblyrequired/shared_invite/enQtMjY3Mjc1OTUwNjEwLWRjMjRkZTg2N2EyNWRjN2YyMDc0YzIyMTUyYzFiNTBmMTU3OGQ1ZjA0MWY0M2IyYjUxZTA4NjRkMWVkNDk2NzY)
|
||||||
|
|
||||||
|
# Deploying the server
|
||||||
|
|
||||||
|
## Linux
|
||||||
|
|
||||||
|
**Installing tools**
|
||||||
|
|
||||||
|
On Ubuntu 16.04:
|
||||||
|
```bash
|
||||||
|
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
|
||||||
|
|
||||||
|
# Build
|
||||||
|
cd Much-Assembly-Required
|
||||||
|
mvn package
|
||||||
|
|
||||||
|
# Run
|
||||||
|
cd target
|
||||||
|
java -jar muchassemblyrequired-*.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
## Windows (tested on Windows 10)
|
||||||
|
|
||||||
|
Installation instructions:
|
||||||
|
1. Download the JDK from [here](http://www.oracle.com/technetwork/java/javase/downloads/index.html).
|
||||||
|
Install the JDK and update your PATH and JAVA_HOME enviroment variables.
|
||||||
|
2. Download Maven from [here](https://maven.apache.org/).
|
||||||
|
Install Maven (following the README) and update your PATH enviroment variable.
|
||||||
|
3. Download Mongo DB Community from [here](https://www.mongodb.com/download-center#community).
|
||||||
|
Install Mongo DB following the instructions [here](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/).
|
||||||
|
Update your PATH enviroment variable.
|
||||||
|
|
||||||
|
Building instructions:
|
||||||
|
```batch
|
||||||
|
:: Builds the server
|
||||||
|
cd Much-Assembly-Required
|
||||||
|
mvn package
|
||||||
|
```
|
||||||
|
|
||||||
|
Running instructions:
|
||||||
|
1. In one Command Prompt window, run Mongo DB:
|
||||||
|
```batch
|
||||||
|
:: Runs Mongo DB
|
||||||
|
mongod
|
||||||
|
```
|
||||||
|
2. In a second Command Prompt window, run the MAR server:
|
||||||
|
```batch
|
||||||
|
:: Runs the MAR server
|
||||||
|
cd Much-Assembly-Required\target
|
||||||
|
java -jar muchassemblyrequired-*.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
1. [Docker Compose](https://docs.docker.com/compose/install/#install-compose) (and dependencies)
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
Once Docker and Docker Compose are installed, you can build and start
|
||||||
|
this application by running the following command inside this
|
||||||
|
application's directory:
|
||||||
|
|
||||||
|
`docker-compose up`
|
||||||
|
|
||||||
|
Make sure to change `mongo_address` in `config.properties` to `mongodb`.
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|||||||
@@ -1,169 +0,0 @@
|
|||||||
package net.simon987.server;
|
|
||||||
|
|
||||||
|
|
||||||
import net.simon987.server.event.GameEventDispatcher;
|
|
||||||
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.plugin.ServerPlugin;
|
|
||||||
import net.simon987.server.user.User;
|
|
||||||
import net.simon987.server.webserver.SocketServer;
|
|
||||||
import org.json.simple.JSONArray;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
public GameServer() {
|
|
||||||
|
|
||||||
this.config = new ServerConfiguration(new File("config.properties"));
|
|
||||||
|
|
||||||
gameUniverse = new GameUniverse(config);
|
|
||||||
pluginManager = new PluginManager();
|
|
||||||
|
|
||||||
//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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(!pluginDir.mkdir()) {
|
|
||||||
LogManager.LOGGER.severe("Couldn't create plugin directory");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
eventDispatcher = new GameEventDispatcher(pluginManager);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
//Process user code
|
|
||||||
for(User user : gameUniverse.getUsers()){
|
|
||||||
|
|
||||||
if(user.getCpu() != null){
|
|
||||||
user.getCpu().reset();
|
|
||||||
user.getCpu().execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Process each worlds
|
|
||||||
for (World world : gameUniverse.getWorlds()) {
|
|
||||||
world.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Save
|
|
||||||
if (gameUniverse.getTime() % config.getInt("save_interval") == 0) {
|
|
||||||
save(new File("save.json"));
|
|
||||||
}
|
|
||||||
|
|
||||||
socketServer.tick();
|
|
||||||
|
|
||||||
LogManager.LOGGER.info("Processed " + gameUniverse.getWorlds().size() + " worlds");
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Save game universe to file in JSON format
|
|
||||||
* @param file JSON file to save
|
|
||||||
*/
|
|
||||||
public void save(File file){
|
|
||||||
|
|
||||||
try {
|
|
||||||
FileWriter fileWriter = new FileWriter(file);
|
|
||||||
|
|
||||||
JSONObject universe = gameUniverse.serialise();
|
|
||||||
|
|
||||||
JSONArray plugins = new JSONArray();
|
|
||||||
|
|
||||||
for(ServerPlugin plugin : pluginManager.getPlugins()){
|
|
||||||
plugins.add(plugin.serialise());
|
|
||||||
}
|
|
||||||
|
|
||||||
universe.put("plugins", plugins);
|
|
||||||
|
|
||||||
fileWriter.write(universe.toJSONString());
|
|
||||||
fileWriter.close();
|
|
||||||
|
|
||||||
LogManager.LOGGER.info("Saved to file " + file.getName());
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public ServerConfiguration getConfig() {
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PluginManager getPluginManager() {
|
|
||||||
return pluginManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSocketServer(SocketServer socketServer) {
|
|
||||||
this.socketServer = socketServer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
package net.simon987.server;
|
|
||||||
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import net.simon987.server.webserver.SocketServer;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.net.InetSocketAddress;
|
|
||||||
|
|
||||||
|
|
||||||
public class Main {
|
|
||||||
public static void main(String[] args){
|
|
||||||
|
|
||||||
|
|
||||||
//TODO: Docs
|
|
||||||
/*
|
|
||||||
* - Intel 8086 p.14 design
|
|
||||||
* - Memory: Storage organisation: From a storage pov, 8086 memory spaces are
|
|
||||||
* organised as identical arrays of 16-bit words
|
|
||||||
* - Microprocessor
|
|
||||||
* - Instruction set
|
|
||||||
* -
|
|
||||||
*/
|
|
||||||
|
|
||||||
//---------------------------------
|
|
||||||
|
|
||||||
//TODO: Random number generator
|
|
||||||
//TODO: favicon
|
|
||||||
//TODO: Email verification
|
|
||||||
//TODO: Real account page
|
|
||||||
// Change/reset password
|
|
||||||
//TODO: Object information Window (Hover, click ?)
|
|
||||||
//TODO: Inventory indicator (Multiple items)
|
|
||||||
//TODO: Software Interrupts (PIC): Interupt flag?
|
|
||||||
/*
|
|
||||||
* - INT/INTO instruction
|
|
||||||
* - IRET instruction
|
|
||||||
*/
|
|
||||||
//TODO: Clock hardware
|
|
||||||
//TODO: Floppy drive hardware (and item?)
|
|
||||||
//TODO: LEA instruction
|
|
||||||
//TODO: XCHG instruction
|
|
||||||
//TODO: SAL/SAR instruction
|
|
||||||
//TODO: ROL/ROR/RCL/RCR instruction
|
|
||||||
//TODO: LOOP/LOOPE/LOOPX/LOOPNE/LOOPNZ ?
|
|
||||||
//TODO: World goto (form)
|
|
||||||
//TODO: Save backup (keep X saves, Zip em)
|
|
||||||
//TODO: Log backup (keep X backups, Zip em)
|
|
||||||
//TODO: More tests
|
|
||||||
//TODO: Handle client disconnects
|
|
||||||
//TODO: Cache objects requests?
|
|
||||||
//TODO: Ability to toggle debug stuff
|
|
||||||
//TODO: Data segment, DB, DW, DD, DQ
|
|
||||||
//TODO: Set client animation speed relative to TICK_LENGTH
|
|
||||||
//TODO: Withdraw animation / action
|
|
||||||
//TODO: Prevent World creation out of bounds, warp around universe
|
|
||||||
//TODO: Multiple Biomass style (and yield, rarity)
|
|
||||||
//TODO: Clean sprites
|
|
||||||
//TODO: Auto-resize
|
|
||||||
//TODO: Battery Hardware
|
|
||||||
|
|
||||||
|
|
||||||
LogManager.initialize();
|
|
||||||
ServerConfiguration config = new ServerConfiguration(new File("config.properties"));
|
|
||||||
|
|
||||||
//Load
|
|
||||||
GameServer.INSTANCE.getGameUniverse().load(new File("save.json"));
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +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.util.Properties;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper for Java Property
|
|
||||||
*/
|
|
||||||
public class ServerConfiguration {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Properties
|
|
||||||
*/
|
|
||||||
private Properties properties;
|
|
||||||
|
|
||||||
public ServerConfiguration(File file) {
|
|
||||||
try {
|
|
||||||
properties = new Properties();
|
|
||||||
|
|
||||||
properties.load(new FileInputStream(file));
|
|
||||||
|
|
||||||
} 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
package net.simon987.server.assembly;
|
|
||||||
|
|
||||||
import net.simon987.server.ServerConfiguration;
|
|
||||||
import net.simon987.server.assembly.exception.AssemblyException;
|
|
||||||
import net.simon987.server.assembly.exception.DuplicateSegmentException;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Result of an assembly attempt
|
|
||||||
*/
|
|
||||||
public class AssemblyResult {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The origin of the program, default is 0x400
|
|
||||||
*/
|
|
||||||
public int origin;
|
|
||||||
/**
|
|
||||||
* A list of labels
|
|
||||||
*/
|
|
||||||
HashMap<String, Character> labels = new HashMap<>(20);
|
|
||||||
/**
|
|
||||||
* List of exceptions encountered during the assembly attempt,
|
|
||||||
* they will be displayed in the editor
|
|
||||||
*/
|
|
||||||
ArrayList<AssemblyException> exceptions = new ArrayList<>(50);
|
|
||||||
/**
|
|
||||||
* Offset of the code segment
|
|
||||||
*/
|
|
||||||
private int codeSegmentOffset;
|
|
||||||
/**
|
|
||||||
* Line of the code segment definition (for editor icons)
|
|
||||||
*/
|
|
||||||
private int codeSegmentLine;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The encoded user code (will be incomplete or invalid if the
|
|
||||||
* assembler encountered an error during assembly)
|
|
||||||
*/
|
|
||||||
public byte[] bytes;
|
|
||||||
/**
|
|
||||||
* Offset of the data segment, default is 0x4000
|
|
||||||
*/
|
|
||||||
private int dataSegmentOffset;
|
|
||||||
/**
|
|
||||||
* Line of the data segment definition (for editor icons)
|
|
||||||
*/
|
|
||||||
private int dataSegmentLine;
|
|
||||||
/**
|
|
||||||
* Whether or not the code segment is set
|
|
||||||
*/
|
|
||||||
private boolean codeSegmentSet = false;
|
|
||||||
/**
|
|
||||||
* Whether or not the data segment is set
|
|
||||||
*/
|
|
||||||
private boolean dataSegmentSet = false;
|
|
||||||
|
|
||||||
AssemblyResult(ServerConfiguration config) {
|
|
||||||
origin = config.getInt("org_offset");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define a segment.
|
|
||||||
*
|
|
||||||
* @param segment Segment to define
|
|
||||||
* @param currentOffset Current offset, in bytes of the segment
|
|
||||||
* @param currentLine Line number of the segment declaration
|
|
||||||
* @throws DuplicateSegmentException when a segment is defined twice
|
|
||||||
*/
|
|
||||||
void defineSegment(Segment segment, int currentLine, int currentOffset) throws DuplicateSegmentException {
|
|
||||||
|
|
||||||
if (segment == Segment.TEXT) {
|
|
||||||
//Code segment
|
|
||||||
|
|
||||||
if (!codeSegmentSet) {
|
|
||||||
codeSegmentOffset = origin + currentOffset;
|
|
||||||
codeSegmentLine = currentLine;
|
|
||||||
codeSegmentSet = true;
|
|
||||||
} else {
|
|
||||||
throw new DuplicateSegmentException(currentLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//Data segment
|
|
||||||
if (!dataSegmentSet) {
|
|
||||||
dataSegmentOffset = origin + currentOffset;
|
|
||||||
dataSegmentLine = currentLine;
|
|
||||||
dataSegmentSet = true;
|
|
||||||
} else {
|
|
||||||
throw new DuplicateSegmentException(currentLine);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,447 +0,0 @@
|
|||||||
package net.simon987.server.assembly;
|
|
||||||
|
|
||||||
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.JSONSerialisable;
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import net.simon987.server.user.User;
|
|
||||||
import org.json.simple.JSONArray;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
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 JSONSerialisable{
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
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 segment. The code starts to get
|
|
||||||
* executed at this address each tick. Defaults to 0x4000
|
|
||||||
*/
|
|
||||||
private int codeSegmentOffset;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 long timeout;
|
|
||||||
|
|
||||||
private int registerSetSize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new CPU
|
|
||||||
*/
|
|
||||||
public CPU(ServerConfiguration config, User user) throws CancelledException{
|
|
||||||
this.config = config;
|
|
||||||
instructionSet = new DefaultInstructionSet();
|
|
||||||
registerSet = new DefaultRegisterSet();
|
|
||||||
attachedHardware = new HashMap<>();
|
|
||||||
codeSegmentOffset = config.getInt("org_offset");
|
|
||||||
|
|
||||||
timeout = config.getInt("user_timeout");
|
|
||||||
|
|
||||||
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));
|
|
||||||
|
|
||||||
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();
|
|
||||||
registerSet.getRegister("SP").setValue(config.getInt("stack_bottom"));
|
|
||||||
registerSet.getRegister("BP").setValue(config.getInt("stack_bottom"));
|
|
||||||
ip = codeSegmentOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void execute() {
|
|
||||||
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
int counter = 0;
|
|
||||||
status.clear();
|
|
||||||
|
|
||||||
registerSetSize = registerSet.size();
|
|
||||||
|
|
||||||
// status.breakFlag = true;
|
|
||||||
while (!status.isBreakFlag()) {
|
|
||||||
counter++;
|
|
||||||
|
|
||||||
if(counter % 1000 == 0){
|
|
||||||
if (System.currentTimeMillis() >= (startTime + timeout)) {
|
|
||||||
LogManager.LOGGER.fine("CPU Timeout " + this + " after " + counter + "instructions (" + timeout + "ms): " + (double) counter / ((double) timeout / 1000) / 1000000 + "MHz");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//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());
|
|
||||||
}
|
|
||||||
double elapsed = (System.currentTimeMillis() - startTime);
|
|
||||||
LogManager.LOGGER.fine("----------\n" + counter + " instruction in " + elapsed + "ms : " + (double) counter / (elapsed / 1000) / 1000000 + "MHz");
|
|
||||||
}
|
|
||||||
|
|
||||||
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(sourceValue, status);
|
|
||||||
instruction.execute(memory, memory.get(ip - 1), status); //For POP instruction
|
|
||||||
} 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), memory, 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), 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), status);
|
|
||||||
} else if (destination <= registerSetSize * 2) {
|
|
||||||
//Destination is [reg]
|
|
||||||
ip++;
|
|
||||||
instruction.execute(memory, registerSet.get(destination - registerSetSize), memory, registerSet.get(source), 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 JSONObject serialise() {
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
|
|
||||||
json.put("memory", memory.serialise());
|
|
||||||
|
|
||||||
json.put("registerSet", registerSet.serialise());
|
|
||||||
json.put("codeSegmentOffset", codeSegmentOffset);
|
|
||||||
|
|
||||||
JSONArray hardwareList = new JSONArray();
|
|
||||||
|
|
||||||
for(Integer address : attachedHardware.keySet()){
|
|
||||||
|
|
||||||
CpuHardware hardware = attachedHardware.get(address);
|
|
||||||
|
|
||||||
JSONObject serialisedHw = hardware.serialise();
|
|
||||||
serialisedHw.put("address", address);
|
|
||||||
hardwareList.add(serialisedHw);
|
|
||||||
}
|
|
||||||
|
|
||||||
json.put("hardware", hardwareList);
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CPU deserialize(JSONObject json, User user) throws CancelledException {
|
|
||||||
|
|
||||||
CPU cpu = new CPU(GameServer.INSTANCE.getConfig(), user);
|
|
||||||
|
|
||||||
cpu.codeSegmentOffset = (int)(long)json.get("codeSegmentOffset");
|
|
||||||
|
|
||||||
JSONArray hardwareList = (JSONArray)json.get("hardware");
|
|
||||||
|
|
||||||
for(JSONObject serialisedHw : (ArrayList<JSONObject>)hardwareList){
|
|
||||||
CpuHardware hw = CpuHardware.deserialize(serialisedHw);
|
|
||||||
hw.setCpu(cpu);
|
|
||||||
cpu.attachHardware(hw, (int)(long)serialisedHw.get("address"));
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu.memory = Memory.deserialize((JSONObject)json.get("memory"));
|
|
||||||
cpu.registerSet = RegisterSet.deserialize((JSONObject) json.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 setCodeSegmentOffset(int codeSegmentOffset) {
|
|
||||||
this.codeSegmentOffset = codeSegmentOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package net.simon987.server.assembly;
|
|
||||||
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.io.CpuHardwareDeserializer;
|
|
||||||
import net.simon987.server.io.JSONSerialisable;
|
|
||||||
import net.simon987.server.plugin.ServerPlugin;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public abstract class CpuHardware implements JSONSerialisable {
|
|
||||||
|
|
||||||
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(JSONObject hwJson){
|
|
||||||
|
|
||||||
for(ServerPlugin plugin : GameServer.INSTANCE.getPluginManager().getPlugins()){
|
|
||||||
|
|
||||||
if(plugin instanceof CpuHardwareDeserializer){
|
|
||||||
CpuHardware hw = ((CpuHardwareDeserializer) plugin).deserializeHardware(hwJson);
|
|
||||||
|
|
||||||
if(hw != null){
|
|
||||||
return hw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
package net.simon987.server.assembly;
|
|
||||||
|
|
||||||
import net.simon987.server.assembly.instruction.*;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default instruction set for the CPU
|
|
||||||
*/
|
|
||||||
public class DefaultInstructionSet implements InstructionSet {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Map of instructions, stored in opcode : Instruction format
|
|
||||||
*/
|
|
||||||
private HashMap<Integer, Instruction> instructionMap = new HashMap<>(32);
|
|
||||||
|
|
||||||
private Instruction defaultInstruction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an empty instruction set
|
|
||||||
*/
|
|
||||||
DefaultInstructionSet() {
|
|
||||||
Instruction nop = new NopInstruction();
|
|
||||||
defaultInstruction = nop;
|
|
||||||
|
|
||||||
add(nop);
|
|
||||||
add(new BrkInstruction());
|
|
||||||
add(new MovInstruction());
|
|
||||||
add(new AddInstruction());
|
|
||||||
add(new SubInstruction());
|
|
||||||
add(new AndInstruction());
|
|
||||||
add(new OrInstruction());
|
|
||||||
add(new ShlInstruction());
|
|
||||||
add(new ShrInstruction());
|
|
||||||
add(new XorInstruction());
|
|
||||||
add(new TestInstruction());
|
|
||||||
add(new CmpInstruction());
|
|
||||||
add(new NegInstruction());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get an instruction from its opcode
|
|
||||||
*
|
|
||||||
* @param opcode opcode of the instruction
|
|
||||||
* @return the instruction, default is not found
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Instruction get(int opcode) {
|
|
||||||
|
|
||||||
Instruction instruction = instructionMap.get(opcode);
|
|
||||||
if(instruction != null){
|
|
||||||
return instruction;
|
|
||||||
} else {
|
|
||||||
// System.out.println("Invalid instruction " + opcode);
|
|
||||||
//Todo: Notify user? Set error flag?
|
|
||||||
return defaultInstruction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a new instruction to the instructionSet
|
|
||||||
*
|
|
||||||
* @param opcode opcode of the instruction
|
|
||||||
* @param instruction Instruction to add
|
|
||||||
*/
|
|
||||||
public void addInstruction(int opcode, Instruction instruction) {
|
|
||||||
instructionMap.put(opcode, instruction);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Instruction get(String mnemonic) {
|
|
||||||
for (Instruction ins : instructionMap.values()) {
|
|
||||||
if (ins.getMnemonic().equalsIgnoreCase(mnemonic)) {
|
|
||||||
return ins;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void add(Instruction instruction) {
|
|
||||||
instructionMap.put(instruction.getOpCode(), instruction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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"));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
package net.simon987.server.assembly;
|
|
||||||
|
|
||||||
|
|
||||||
import net.simon987.server.io.JSONSerialisable;
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.zip.Deflater;
|
|
||||||
import java.util.zip.DeflaterOutputStream;
|
|
||||||
import java.util.zip.Inflater;
|
|
||||||
import java.util.zip.InflaterOutputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the available memory for a CPU in the game universe
|
|
||||||
*/
|
|
||||||
public class Memory implements Target, JSONSerialisable {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contents of the memory
|
|
||||||
*/
|
|
||||||
private byte[] bytes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an empty Memory object
|
|
||||||
*
|
|
||||||
* @param size Size of the memory, in words
|
|
||||||
*/
|
|
||||||
public Memory(int size) {
|
|
||||||
bytes = new byte[size];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the value at an address
|
|
||||||
*
|
|
||||||
* @param address Address of the value
|
|
||||||
* @return 16-bit value at the specified address
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int get(int address) {
|
|
||||||
address = (char)address * 2; //Because our Memory is only divisible by 16bits
|
|
||||||
|
|
||||||
if (address < 0 || address + 2 > bytes.length) {
|
|
||||||
LogManager.LOGGER.info("DEBUG: Trying to get memory out of bounds " + address);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (((bytes[address] & 0xFF) << 8) | (bytes[address + 1] & 0xFF));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write x words from an array at an offset
|
|
||||||
*/
|
|
||||||
public boolean write(int offset, byte[] bytes, int count) {
|
|
||||||
|
|
||||||
offset = (char)offset * 2;
|
|
||||||
|
|
||||||
|
|
||||||
if (offset + count > this.bytes.length || count < 0 || offset < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
System.arraycopy(bytes, 0, this.bytes, offset, count);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the value at an address
|
|
||||||
*
|
|
||||||
* @param address address of the value to change
|
|
||||||
* @param value 16-bit value to set
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void set(int address, int value) {
|
|
||||||
|
|
||||||
address = (char)address * 2;
|
|
||||||
|
|
||||||
|
|
||||||
if (address < 0 || address + 2 > bytes.length) {
|
|
||||||
LogManager.LOGGER.info("DEBUG: Trying to set memory out of bounds: " + address);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes[address] = (byte) ((value >> 8) & 0xFF);
|
|
||||||
bytes[address + 1] = (byte) (value & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill the memory with 0s
|
|
||||||
*/
|
|
||||||
public void clear() {
|
|
||||||
Arrays.fill(bytes, (byte) 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get byte array of the Memory object
|
|
||||||
*/
|
|
||||||
public byte[] getBytes() {
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject serialise() {
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
|
|
||||||
try {
|
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
|
||||||
Deflater compressor = new Deflater(Deflater.BEST_COMPRESSION, true);
|
|
||||||
DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(stream, compressor);
|
|
||||||
deflaterOutputStream.write(bytes);
|
|
||||||
deflaterOutputStream.close();
|
|
||||||
byte[] compressedBytes = stream.toByteArray();
|
|
||||||
|
|
||||||
json.put("zipBytes", new String(Base64.getEncoder().encode(compressedBytes)));
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
//To deflate
|
|
||||||
|
|
||||||
/*
|
|
||||||
ByteArrayOutputStream stream2 = new ByteArrayOutputStream();
|
|
||||||
Inflater decompresser = new Inflater(true);
|
|
||||||
InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(stream2, decompresser);
|
|
||||||
inflaterOutputStream.write(output);
|
|
||||||
inflaterOutputStream.close();
|
|
||||||
byte[] output2 = stream2.toByteArray();
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Memory deserialize(JSONObject json){
|
|
||||||
|
|
||||||
Memory memory = new Memory(0);
|
|
||||||
byte[] compressedBytes = Base64.getDecoder().decode((String)json.get("zipBytes"));
|
|
||||||
|
|
||||||
try {
|
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
|
||||||
Inflater decompressor = new Inflater(true);
|
|
||||||
InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(baos, decompressor);
|
|
||||||
inflaterOutputStream.write(compressedBytes);
|
|
||||||
inflaterOutputStream.close();
|
|
||||||
|
|
||||||
memory.bytes = baos.toByteArray();
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return memory;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,191 +0,0 @@
|
|||||||
package net.simon987.server.assembly;
|
|
||||||
|
|
||||||
|
|
||||||
import net.simon987.server.io.JSONSerialisable;
|
|
||||||
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, JSONSerialisable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 JSONObject serialise() {
|
|
||||||
JSONArray registers = new JSONArray();
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
json.put("registers", registers);
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
package net.simon987.server.assembly.exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Threw when a user attempts to define the same section twice
|
|
||||||
*/
|
|
||||||
public class DuplicateSegmentException extends AssemblyException {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message of the exception
|
|
||||||
*/
|
|
||||||
private static final String message = "Segments can only be defined once";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new Duplicate Segment Exception
|
|
||||||
*/
|
|
||||||
public DuplicateSegmentException(int line) {
|
|
||||||
super(message, line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Divide instruction.
|
|
||||||
* <p>
|
|
||||||
* DIV C
|
|
||||||
* A = Y:A / C
|
|
||||||
* Y = Y:A % C
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public class DivInstruction extends Instruction {
|
|
||||||
|
|
||||||
public static final int OPCODE = 24;
|
|
||||||
|
|
||||||
private CPU cpu;
|
|
||||||
|
|
||||||
public DivInstruction(CPU cpu) {
|
|
||||||
super("div", OPCODE);
|
|
||||||
this.cpu = cpu;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Status execute(Target src, int srcIndex, Status status) {
|
|
||||||
|
|
||||||
//Source = Y:A
|
|
||||||
int source = ((((char)cpu.getRegisterSet().getRegister("Y").getValue() & 0xFFFF) << 16)) |
|
|
||||||
((char)cpu.getRegisterSet().getRegister("A").getValue() & 0xFFFF);
|
|
||||||
|
|
||||||
if (src.get(srcIndex) == 0) {
|
|
||||||
//Division by 0
|
|
||||||
status.setBreakFlag(true);
|
|
||||||
status.setErrorFlag(true);
|
|
||||||
} else {
|
|
||||||
cpu.getRegisterSet().getRegister("A").setValue((char)(source / (char)src.get(srcIndex)));
|
|
||||||
cpu.getRegisterSet().getRegister("Y").setValue((char)(source % (char)src.get(srcIndex)));
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Status execute(int src, Status status) {
|
|
||||||
|
|
||||||
|
|
||||||
//Source = Y:A
|
|
||||||
int source = ((((char)cpu.getRegisterSet().getRegister("Y").getValue() & 0xFFFF) << 16)) |
|
|
||||||
((char)cpu.getRegisterSet().getRegister("A").getValue() & 0xFFFF);
|
|
||||||
|
|
||||||
if (src == 0) {
|
|
||||||
//Division by 0
|
|
||||||
status.setBreakFlag(true);
|
|
||||||
status.setErrorFlag(true);
|
|
||||||
} else {
|
|
||||||
cpu.getRegisterSet().getRegister("A").setValue((char)(source / (char)src));
|
|
||||||
cpu.getRegisterSet().getRegister("Y").setValue((char)(source % (char)src));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package net.simon987.server.assembly.instruction;
|
|
||||||
|
|
||||||
import net.simon987.server.assembly.Instruction;
|
|
||||||
import net.simon987.server.assembly.Status;
|
|
||||||
import net.simon987.server.assembly.Target;
|
|
||||||
|
|
||||||
public class NegInstruction extends Instruction {
|
|
||||||
|
|
||||||
public static final int OPCODE = 25;
|
|
||||||
|
|
||||||
public NegInstruction() {
|
|
||||||
super("neg", OPCODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Status execute(Target dst, int dstIndex, Status status) {
|
|
||||||
dst.set(dstIndex, -dst.get(dstIndex));
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package net.simon987.server.event;
|
|
||||||
|
|
||||||
import net.simon987.server.plugin.PluginManager;
|
|
||||||
import net.simon987.server.plugin.ServerPlugin;
|
|
||||||
|
|
||||||
|
|
||||||
public class GameEventDispatcher {
|
|
||||||
|
|
||||||
private PluginManager pluginManager;
|
|
||||||
|
|
||||||
public GameEventDispatcher(PluginManager pluginManager) {
|
|
||||||
this.pluginManager = pluginManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispatch(GameEvent event){
|
|
||||||
for(ServerPlugin plugin: pluginManager.getPlugins()){
|
|
||||||
for(GameEventListener listener : plugin.getListeners()){
|
|
||||||
if(event.getClass().equals(listener.getListenedEventType())){
|
|
||||||
listener.handle(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package net.simon987.server.event;
|
|
||||||
|
|
||||||
import net.simon987.server.game.World;
|
|
||||||
|
|
||||||
public class WorldGenerationEvent extends GameEvent {
|
|
||||||
|
|
||||||
public WorldGenerationEvent(World world) {
|
|
||||||
setSource(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
public World getWorld(){
|
|
||||||
return (World)getSource();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package net.simon987.server.game;
|
|
||||||
|
|
||||||
import net.simon987.server.user.User;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public interface ControllableUnit {
|
|
||||||
|
|
||||||
int getObjectId();
|
|
||||||
|
|
||||||
void setKeyboardBuffer(ArrayList<Integer> kbBuffer);
|
|
||||||
|
|
||||||
void setParent(User user);
|
|
||||||
|
|
||||||
ArrayList<Integer> getKeyboardBuffer();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package net.simon987.server.game;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Direction of a game object in a 4-direction grid-based
|
|
||||||
* area
|
|
||||||
*/
|
|
||||||
public enum Direction {
|
|
||||||
/**
|
|
||||||
* North, up
|
|
||||||
*/
|
|
||||||
NORTH,
|
|
||||||
/**
|
|
||||||
* East, right
|
|
||||||
*/
|
|
||||||
EAST,
|
|
||||||
/**
|
|
||||||
* South, bottom
|
|
||||||
*/
|
|
||||||
SOUTH,
|
|
||||||
/**
|
|
||||||
* West, left
|
|
||||||
*/
|
|
||||||
WEST;
|
|
||||||
|
|
||||||
public static Direction getDirection(int x) {
|
|
||||||
switch (x) {
|
|
||||||
case 0:
|
|
||||||
return NORTH;
|
|
||||||
case 1:
|
|
||||||
return EAST;
|
|
||||||
case 2:
|
|
||||||
return SOUTH;
|
|
||||||
case 3:
|
|
||||||
return WEST;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
package net.simon987.server.game;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Types of GameEffects
|
|
||||||
*/
|
|
||||||
public enum EffectType {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Warning icon
|
|
||||||
*/
|
|
||||||
WARNING,
|
|
||||||
/**
|
|
||||||
* Error icon
|
|
||||||
*/
|
|
||||||
ERROR,
|
|
||||||
/**
|
|
||||||
* Dig particle effect
|
|
||||||
*/
|
|
||||||
DIG,
|
|
||||||
/**
|
|
||||||
* 'A' Icon
|
|
||||||
*/
|
|
||||||
A_EMOTE
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package net.simon987.server.game;
|
|
||||||
|
|
||||||
import net.simon987.server.io.JSONSerialisable;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a game effect in a World (e.g. Particles made when digging, Error animation, Attack effects etc..)
|
|
||||||
* <br>
|
|
||||||
* These effects are purely visual and could be changed or ignored by the client
|
|
||||||
*/
|
|
||||||
public class GameEffect implements JSONSerialisable{
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of the effect
|
|
||||||
*/
|
|
||||||
private EffectType type;
|
|
||||||
|
|
||||||
private int x;
|
|
||||||
|
|
||||||
private int y;
|
|
||||||
|
|
||||||
public GameEffect(EffectType type, int x, int y) {
|
|
||||||
this.type = type;
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject serialise() {
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
|
|
||||||
json.put("x", x);
|
|
||||||
json.put("y", y);
|
|
||||||
json.put("type", type);
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EffectType getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setType(EffectType type) {
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
package net.simon987.server.game;
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.io.GameObjectDeserializer;
|
|
||||||
import net.simon987.server.io.JSONSerialisable;
|
|
||||||
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 {
|
|
||||||
|
|
||||||
private boolean dead;
|
|
||||||
/**
|
|
||||||
* Object's unique identifier
|
|
||||||
*/
|
|
||||||
private int 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 = GameServer.INSTANCE.getGameUniverse().getWorld(world.getX() - 1, world.getY());
|
|
||||||
|
|
||||||
if(leftWorld != null){
|
|
||||||
world.getGameObjects().remove(this);
|
|
||||||
leftWorld.getGameObjects().add(this);
|
|
||||||
setWorld(leftWorld);
|
|
||||||
|
|
||||||
x = World.WORLD_SIZE - 1;
|
|
||||||
}
|
|
||||||
} else if(newX >= World.WORLD_SIZE) {
|
|
||||||
//Move object to adjacent World (right)
|
|
||||||
World rightWorld = GameServer.INSTANCE.getGameUniverse().getWorld(world.getX() + 1, world.getY());
|
|
||||||
|
|
||||||
if(rightWorld != null){
|
|
||||||
world.getGameObjects().remove(this);
|
|
||||||
rightWorld.getGameObjects().add(this);
|
|
||||||
setWorld(rightWorld);
|
|
||||||
|
|
||||||
x = 0;
|
|
||||||
}
|
|
||||||
} else if (newY < 0) {
|
|
||||||
//Move object to adjacent World (down)
|
|
||||||
World downWorld = GameServer.INSTANCE.getGameUniverse().getWorld(world.getX(), world.getY() - 1);
|
|
||||||
|
|
||||||
if(downWorld != null){
|
|
||||||
world.getGameObjects().remove(this);
|
|
||||||
downWorld.getGameObjects().add(this);
|
|
||||||
setWorld(downWorld);
|
|
||||||
|
|
||||||
y = World.WORLD_SIZE - 1;
|
|
||||||
}
|
|
||||||
} else if(newY >= World.WORLD_SIZE) {
|
|
||||||
//Move object to adjacent World (up)
|
|
||||||
World upWorld = GameServer.INSTANCE.getGameUniverse().getWorld(world.getX(), world.getY() + 1);
|
|
||||||
|
|
||||||
if(upWorld != null){
|
|
||||||
world.getGameObjects().remove(this);
|
|
||||||
upWorld.getGameObjects().add(this);
|
|
||||||
setWorld(upWorld);
|
|
||||||
|
|
||||||
y = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Check collision
|
|
||||||
else if (!world.isTileBlocked(newX, newY)) {
|
|
||||||
//Tile is passable
|
|
||||||
x = newX;
|
|
||||||
y = newY;
|
|
||||||
} else {
|
|
||||||
//Display error when object is trying to walk in a wall
|
|
||||||
//TODO Add emote here
|
|
||||||
//System.out.println("DEBUG: FAILED walk");
|
|
||||||
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 int getObjectId() {
|
|
||||||
return objectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setObjectId(int 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(JSONObject objJson) {
|
|
||||||
|
|
||||||
for(ServerPlugin plugin : GameServer.INSTANCE.getPluginManager().getPlugins()){
|
|
||||||
|
|
||||||
if(plugin instanceof GameObjectDeserializer){
|
|
||||||
GameObject object = ((GameObjectDeserializer) plugin).deserializeObject(objJson);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
package net.simon987.server.game;
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.ServerConfiguration;
|
|
||||||
import net.simon987.server.assembly.Assembler;
|
|
||||||
import net.simon987.server.assembly.AssemblyResult;
|
|
||||||
import net.simon987.server.assembly.CPU;
|
|
||||||
import net.simon987.server.assembly.exception.CancelledException;
|
|
||||||
import net.simon987.server.io.JSONSerialisable;
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import net.simon987.server.user.User;
|
|
||||||
import org.json.simple.JSONArray;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
import org.json.simple.parser.JSONParser;
|
|
||||||
import org.json.simple.parser.ParseException;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class GameUniverse implements JSONSerialisable{
|
|
||||||
|
|
||||||
private ArrayList<World> worlds;
|
|
||||||
private ArrayList<User> users;
|
|
||||||
private WorldGenerator worldGenerator;
|
|
||||||
|
|
||||||
private long time;
|
|
||||||
|
|
||||||
private int nextObjectId = 0;
|
|
||||||
|
|
||||||
public GameUniverse(ServerConfiguration config) {
|
|
||||||
|
|
||||||
worlds = new ArrayList<>(32);
|
|
||||||
users = new ArrayList<>(16);
|
|
||||||
|
|
||||||
worldGenerator = new WorldGenerator(config);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getTime() {
|
|
||||||
return time;
|
|
||||||
}
|
|
||||||
|
|
||||||
public World getWorld(int x, int y) {
|
|
||||||
|
|
||||||
for (World world : worlds) {
|
|
||||||
if (world.getX() == x && world.getY() == y) {
|
|
||||||
return world;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//World does not exist
|
|
||||||
LogManager.LOGGER.severe("Trying to read a World that does not exist!");
|
|
||||||
|
|
||||||
World world = createWorld(x,y);
|
|
||||||
|
|
||||||
worlds.add(world);
|
|
||||||
|
|
||||||
return world;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public World createWorld(int x, int y) {
|
|
||||||
|
|
||||||
World world = null;
|
|
||||||
try {
|
|
||||||
world = worldGenerator.generateWorld(x, y);
|
|
||||||
|
|
||||||
|
|
||||||
} catch (CancelledException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return world;
|
|
||||||
}
|
|
||||||
|
|
||||||
public User getUser(String username) {
|
|
||||||
|
|
||||||
for (User user : users) {
|
|
||||||
if (user.getUsername().equals(username)) {
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public User getOrCreateUser(String username, boolean makeControlledUnit){
|
|
||||||
User user = getUser(username);
|
|
||||||
|
|
||||||
if(user != null) {
|
|
||||||
return user;
|
|
||||||
} else {
|
|
||||||
|
|
||||||
LogManager.LOGGER.info("Creating new User: " + username);
|
|
||||||
|
|
||||||
try {
|
|
||||||
if(makeControlledUnit) {
|
|
||||||
user = new User();
|
|
||||||
user.setCpu(new CPU(GameServer.INSTANCE.getConfig(), user));
|
|
||||||
user.setUserCode(GameServer.INSTANCE.getConfig().getString("new_user_code"));
|
|
||||||
|
|
||||||
//Compile user code
|
|
||||||
AssemblyResult ar = new Assembler(user.getCpu().getInstructionSet(), user.getCpu().getRegisterSet(),
|
|
||||||
GameServer.INSTANCE.getConfig()).parse(user.getUserCode());
|
|
||||||
|
|
||||||
user.getCpu().getMemory().clear();
|
|
||||||
|
|
||||||
//Write assembled code to mem
|
|
||||||
user.getCpu().getMemory().write((short) ar.origin, ar.bytes, ar.bytes.length);
|
|
||||||
user.getCpu().setCodeSegmentOffset(ar.origin);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
user = new User(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
user.setUsername(username);
|
|
||||||
|
|
||||||
users.add(user);
|
|
||||||
|
|
||||||
return user;
|
|
||||||
|
|
||||||
} catch (CancelledException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public GameObject getObject(int id) {
|
|
||||||
|
|
||||||
for (World world : worlds) {
|
|
||||||
for(GameObject object : world.getGameObjects()){
|
|
||||||
if(object.getObjectId() == id){
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void incrementTime(){
|
|
||||||
time++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<World> getWorlds() {
|
|
||||||
return worlds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<User> getUsers() {
|
|
||||||
return users;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject serialise() {
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
|
|
||||||
JSONArray worlds = new JSONArray();
|
|
||||||
|
|
||||||
for(World world : this.worlds){
|
|
||||||
worlds.add(world.serialise());
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONArray users = new JSONArray();
|
|
||||||
for(User user : this.users){
|
|
||||||
if (!user.isGuest()) {
|
|
||||||
users.add(user.serialise());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
json.put("users", users);
|
|
||||||
json.put("worlds", worlds);
|
|
||||||
json.put("time", time);
|
|
||||||
json.put("nextObjectId", nextObjectId);
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load game universe from JSON save file
|
|
||||||
* @param file JSON save file
|
|
||||||
*/
|
|
||||||
public void load(File file){
|
|
||||||
|
|
||||||
JSONParser parser = new JSONParser();
|
|
||||||
|
|
||||||
if (file.isFile()) {
|
|
||||||
try {
|
|
||||||
|
|
||||||
FileReader reader = new FileReader(file);
|
|
||||||
JSONObject universeJson = (JSONObject) parser.parse(reader);
|
|
||||||
|
|
||||||
time = (long) universeJson.get("time");
|
|
||||||
nextObjectId = (int) (long) universeJson.get("nextObjectId");
|
|
||||||
|
|
||||||
for (JSONObject worldJson : (ArrayList<JSONObject>) universeJson.get("worlds")) {
|
|
||||||
worlds.add(World.deserialize(worldJson));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (JSONObject userJson : (ArrayList<JSONObject>) universeJson.get("users")) {
|
|
||||||
users.add(User.deserialize(userJson));
|
|
||||||
}
|
|
||||||
|
|
||||||
LogManager.LOGGER.info("Loaded " + worlds.size() + " worlds from file");
|
|
||||||
|
|
||||||
reader.close();
|
|
||||||
|
|
||||||
} catch (IOException | ParseException | CancelledException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LogManager.LOGGER.severe("Couldn't load save file save.json, creating empty game universe.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNextObjectId() {
|
|
||||||
return ++nextObjectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getGuestUsername() {
|
|
||||||
int i = 1;
|
|
||||||
|
|
||||||
while (i < 1000) { //todo get Max guest user cap from config
|
|
||||||
if(getUser("guest" + String.valueOf(i)) != null) {
|
|
||||||
i++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "guest" + String.valueOf(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeUser(User user) {
|
|
||||||
users.remove(user);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +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_*)
|
|
||||||
* @return true is the take item action executed properly, true also means that the desired item
|
|
||||||
* was removed from the inventory
|
|
||||||
*/
|
|
||||||
boolean takeItem(int item);
|
|
||||||
}
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
package net.simon987.server.game;
|
|
||||||
|
|
||||||
|
|
||||||
import net.simon987.server.io.JSONSerialisable;
|
|
||||||
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 {
|
|
||||||
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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("zipTerrain", new String(Base64.getEncoder().encode(compressedBytes)));
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TileMap deserialize(JSONObject object) {
|
|
||||||
|
|
||||||
TileMap tileMap = new TileMap(World.WORLD_SIZE, World.WORLD_SIZE);
|
|
||||||
|
|
||||||
|
|
||||||
byte[] compressedBytes = Base64.getDecoder().decode((String)object.get("zipTerrain"));
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,233 +0,0 @@
|
|||||||
package net.simon987.server.game;
|
|
||||||
|
|
||||||
import net.simon987.server.game.pathfinding.Pathfinder;
|
|
||||||
import net.simon987.server.io.JSONSerialisable;
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import org.json.simple.JSONArray;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class World implements JSONSerialisable{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Size of the side of a world
|
|
||||||
*/
|
|
||||||
public static final int WORLD_SIZE = 16;
|
|
||||||
|
|
||||||
private static final char INFO_BLOCKED = 0x8000;
|
|
||||||
private static final char INFO_IRON = 0x0200;
|
|
||||||
private static final char INFO_COPPER = 0x0100;
|
|
||||||
|
|
||||||
private int x;
|
|
||||||
private int y;
|
|
||||||
|
|
||||||
private TileMap tileMap;
|
|
||||||
|
|
||||||
private ArrayList<GameObject> gameObjects = new ArrayList<>(16);
|
|
||||||
|
|
||||||
public World(int x, int y, TileMap tileMap) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.tileMap = tileMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
private World(){
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public TileMap getTileMap() {
|
|
||||||
return tileMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a tile is blocked, either by a game object or an impassable tile type
|
|
||||||
*/
|
|
||||||
public boolean isTileBlocked(int x, int y) {
|
|
||||||
|
|
||||||
return getGameObjectsAt(x, y).size() > 0 || tileMap.getTileAt(x, y) == TileMap.WALL_TILE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getX() {
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getY() {
|
|
||||||
return y;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<GameObject> getGameObjects() {
|
|
||||||
return gameObjects;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void update(){
|
|
||||||
|
|
||||||
ArrayList<GameObject> gameObjects_ = new ArrayList<>(gameObjects);
|
|
||||||
|
|
||||||
for(GameObject object : gameObjects_){
|
|
||||||
if(object.isDead()){
|
|
||||||
gameObjects.remove(object);
|
|
||||||
LogManager.LOGGER.fine("Removed object " + object + " id: " + object.getObjectId());
|
|
||||||
}
|
|
||||||
if (object instanceof Updatable) {
|
|
||||||
((Updatable) object).update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSONObject serialise() {
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
|
|
||||||
JSONArray objects = new JSONArray();
|
|
||||||
for(GameObject obj : gameObjects){
|
|
||||||
objects.add(obj.serialise());
|
|
||||||
}
|
|
||||||
json.put("objects", objects);
|
|
||||||
|
|
||||||
json.put("terrain", tileMap.serialise());
|
|
||||||
|
|
||||||
json.put("x", x);
|
|
||||||
json.put("y", y);
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
|
|
||||||
String str = "World (" + x + ", " + y + ")\n";
|
|
||||||
int[][] tileMap = this.tileMap.getTiles();
|
|
||||||
|
|
||||||
for (int x = 0; x < WORLD_SIZE; x++) {
|
|
||||||
for (int y = 0; y < WORLD_SIZE; y++) {
|
|
||||||
str += tileMap[x][y] + " ";
|
|
||||||
}
|
|
||||||
str += "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return str;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static World deserialize(JSONObject json) {
|
|
||||||
World world = new World();
|
|
||||||
world.x = (int)(long)json.get("x");
|
|
||||||
world.y = (int)(long)json.get("y");
|
|
||||||
|
|
||||||
world.tileMap = TileMap.deserialize((JSONObject)json.get("terrain"));
|
|
||||||
|
|
||||||
for(JSONObject objJson : (ArrayList<JSONObject>)json.get("objects")){
|
|
||||||
|
|
||||||
GameObject object = GameObject.deserialize(objJson);
|
|
||||||
|
|
||||||
object.setWorld(world);
|
|
||||||
world.gameObjects.add(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
return world;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a binary representation of the map as an array of 16-bit bit fields, one word for each
|
|
||||||
* tile.
|
|
||||||
* <p>
|
|
||||||
* todo Performance cache this?
|
|
||||||
*/
|
|
||||||
public char[][] getMapInfo() {
|
|
||||||
|
|
||||||
char[][] mapInfo = new char[World.WORLD_SIZE][World.WORLD_SIZE];
|
|
||||||
int[][] tiles = tileMap.getTiles();
|
|
||||||
|
|
||||||
//Tile
|
|
||||||
for (int y = 0; y < World.WORLD_SIZE; y++) {
|
|
||||||
for (int x = 0; x < World.WORLD_SIZE; x++) {
|
|
||||||
|
|
||||||
if (tiles[x][y] == TileMap.PLAIN_TILE) {
|
|
||||||
|
|
||||||
mapInfo[x][y] = 0;
|
|
||||||
} else if (tiles[x][y] == TileMap.WALL_TILE) {
|
|
||||||
|
|
||||||
mapInfo[x][y] = INFO_BLOCKED;
|
|
||||||
} else if (tiles[x][y] == TileMap.COPPER_TILE) {
|
|
||||||
|
|
||||||
mapInfo[x][y] = INFO_COPPER;
|
|
||||||
} else if (tiles[x][y] == TileMap.IRON_TILE) {
|
|
||||||
|
|
||||||
mapInfo[x][y] = INFO_IRON;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Objects
|
|
||||||
for (GameObject obj : this.gameObjects) {
|
|
||||||
mapInfo[obj.getX()][obj.getY()] |= obj.getMapInfo();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapInfo;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a random tile that is empty and passable
|
|
||||||
* The function ensures that a object spawned there will not be trapped
|
|
||||||
* and will be able to leave the World
|
|
||||||
* <br>
|
|
||||||
* Note: This function is quite expensive and shouldn't be used
|
|
||||||
* by some CpuHardware in its current state
|
|
||||||
*
|
|
||||||
* @return random non-blocked tile
|
|
||||||
*/
|
|
||||||
public Point getRandomPassableTile(){
|
|
||||||
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(!isTileBlocked(rx, ry)){
|
|
||||||
|
|
||||||
Object path = Pathfinder.findPath(this, rx, ry, 0, 6, 0);
|
|
||||||
|
|
||||||
if(path != null) {
|
|
||||||
return new Point(rx, ry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the list of game objects at a location
|
|
||||||
*
|
|
||||||
* @param x X coordinate on the World
|
|
||||||
* @param y Y coordinate on the World
|
|
||||||
* @return the list of game objects at a location
|
|
||||||
*/
|
|
||||||
public ArrayList<GameObject> getGameObjectsAt(int x, int y) {
|
|
||||||
|
|
||||||
ArrayList<GameObject> gameObjects = new ArrayList<>(2);
|
|
||||||
|
|
||||||
for (GameObject obj : this.gameObjects) {
|
|
||||||
|
|
||||||
if (obj.isAt(x, y)) {
|
|
||||||
gameObjects.add(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return gameObjects;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package net.simon987.server.io;
|
|
||||||
|
|
||||||
import net.simon987.server.assembly.CpuHardware;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public interface CpuHardwareDeserializer {
|
|
||||||
|
|
||||||
|
|
||||||
CpuHardware deserializeHardware(JSONObject hwJson);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package net.simon987.server.io;
|
|
||||||
|
|
||||||
import net.simon987.server.game.GameObject;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public interface GameObjectDeserializer {
|
|
||||||
|
|
||||||
GameObject deserializeObject(JSONObject object);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package net.simon987.server.io;
|
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public interface JSONSerialisable {
|
|
||||||
|
|
||||||
JSONObject serialise();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package net.simon987.server.logging;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.logging.Formatter;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.LogRecord;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic formatter for the game logging
|
|
||||||
*/
|
|
||||||
public class GenericFormatter extends Formatter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String format(LogRecord record) {
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
|
|
||||||
if (record.getLevel() == Level.FINE) {
|
|
||||||
//Chat message, maximum 50 char per line
|
|
||||||
if (record.getMessage().length() > 50) {
|
|
||||||
sb.append(record.getMessage().substring(0, 50));
|
|
||||||
sb.append('\n');
|
|
||||||
sb.append(record.getMessage().substring(50));
|
|
||||||
} else {
|
|
||||||
sb.append(record.getMessage());
|
|
||||||
}
|
|
||||||
sb.append('\n');
|
|
||||||
} else {
|
|
||||||
//Regular record
|
|
||||||
Date date = new Date();
|
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd HH:mm:ss:SSS"); //ex. 11/25 22:03:59:010
|
|
||||||
|
|
||||||
sb.append(String.format("[%s] [%s] %s", sdf.format(date), record.getLevel(), record.getMessage()));
|
|
||||||
sb.append('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package net.simon987.server.logging;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.logging.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility class to manage log entries
|
|
||||||
*/
|
|
||||||
public class LogManager {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Singleton Logger
|
|
||||||
*/
|
|
||||||
public final static Logger LOGGER = Logger.getLogger("mar");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialises the logger
|
|
||||||
*/
|
|
||||||
public static void initialize() {
|
|
||||||
LOGGER.setUseParentHandlers(false);
|
|
||||||
|
|
||||||
Handler handler = new ConsoleHandler();
|
|
||||||
handler.setLevel(Level.ALL);
|
|
||||||
handler.setFormatter(new GenericFormatter());
|
|
||||||
|
|
||||||
try {
|
|
||||||
Handler fileHandler = new FileHandler("mar.log");
|
|
||||||
fileHandler.setLevel(Level.ALL);
|
|
||||||
fileHandler.setFormatter(new GenericFormatter());
|
|
||||||
|
|
||||||
LOGGER.addHandler(handler);
|
|
||||||
LOGGER.addHandler(fileHandler);
|
|
||||||
LOGGER.setLevel(Level.ALL);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package net.simon987.server.plugin;
|
|
||||||
|
|
||||||
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){
|
|
||||||
|
|
||||||
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 config = new Properties();
|
|
||||||
config.load(stream);
|
|
||||||
|
|
||||||
//Load the plugin
|
|
||||||
ClassLoader loader = URLClassLoader.newInstance(new URL[] {pluginFile.toURI().toURL() });
|
|
||||||
Class<?> aClass = Class.forName(config.getProperty("classpath"), true, loader);
|
|
||||||
Class<? extends ServerPlugin> pluginClass = aClass.asSubclass(ServerPlugin.class);
|
|
||||||
Constructor<? extends ServerPlugin> constructor = pluginClass.getConstructor();
|
|
||||||
|
|
||||||
ServerPlugin plugin = constructor.newInstance();
|
|
||||||
plugin.setName(config.getProperty("name"));
|
|
||||||
plugin.setVersion(config.getProperty("version"));
|
|
||||||
|
|
||||||
LogManager.LOGGER.info("Loaded " + plugin.name + " V" + plugin.version);
|
|
||||||
|
|
||||||
//Add it to the list
|
|
||||||
plugins.add(plugin);
|
|
||||||
|
|
||||||
//Init the plugin
|
|
||||||
plugin.init();
|
|
||||||
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
package net.simon987.server.plugin;
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
package net.simon987.server.user;
|
|
||||||
|
|
||||||
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.JSONSerialisable;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a User (or player) of the game
|
|
||||||
*/
|
|
||||||
public class User implements JSONSerialisable{
|
|
||||||
|
|
||||||
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 JSONObject serialise() {
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
|
|
||||||
json.put("username", username);
|
|
||||||
json.put("code", userCode);
|
|
||||||
json.put("controlledUnit", controlledUnit.getObjectId());
|
|
||||||
json.put("cpu", cpu.serialise());
|
|
||||||
|
|
||||||
return json;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static User deserialize(JSONObject userJson) throws CancelledException {
|
|
||||||
|
|
||||||
User user = new User((ControllableUnit)GameServer.INSTANCE.getGameUniverse().getObject((int)(long)userJson.get("controlledUnit")));
|
|
||||||
user.username = (String)userJson.get("username");
|
|
||||||
user.userCode = (String)userJson.get("code");
|
|
||||||
|
|
||||||
user.getControlledUnit().setParent(user);
|
|
||||||
|
|
||||||
user.cpu = CPU.deserialize((JSONObject)userJson.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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
package net.simon987.server.webserver;
|
|
||||||
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public class CodeRequestHandler implements MessageHandler {
|
|
||||||
@Override
|
|
||||||
public void handle(OnlineUser user, JSONObject json) {
|
|
||||||
|
|
||||||
if(json.get("t").equals("codeRequest")){
|
|
||||||
|
|
||||||
LogManager.LOGGER.info("(WS) Code request from " + user.getUser().getUsername());
|
|
||||||
|
|
||||||
if(user.isGuest()) {
|
|
||||||
|
|
||||||
JSONObject response = new JSONObject();
|
|
||||||
|
|
||||||
response.put("t", "code");
|
|
||||||
response.put("code", "; Create a free account to control your own Cubot with assembly language!"); //todo load from config
|
|
||||||
|
|
||||||
user.getWebSocket().send(response.toJSONString());
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
JSONObject response = new JSONObject();
|
|
||||||
|
|
||||||
response.put("t", "code");
|
|
||||||
response.put("code", user.getUser().getUserCode());
|
|
||||||
|
|
||||||
user.getWebSocket().send(response.toJSONString());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package net.simon987.server.webserver;
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.assembly.Assembler;
|
|
||||||
import net.simon987.server.assembly.AssemblyResult;
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public class CodeUploadHandler implements MessageHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(OnlineUser user, JSONObject json) {
|
|
||||||
if(json.get("t").equals("uploadCode")){
|
|
||||||
|
|
||||||
LogManager.LOGGER.info("(WS) Code upload from " + user.getUser().getUsername());
|
|
||||||
|
|
||||||
if(user.isGuest()) {
|
|
||||||
//Ignore
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//TODO Should we wait at the end of the tick to modify the CPU ?
|
|
||||||
user.getUser().setUserCode((String)json.get("code"));
|
|
||||||
|
|
||||||
AssemblyResult ar = new Assembler(user.getUser().getCpu().getInstructionSet(),
|
|
||||||
user.getUser().getCpu().getRegisterSet(),
|
|
||||||
GameServer.INSTANCE.getConfig()).parse(user.getUser().getUserCode());
|
|
||||||
|
|
||||||
user.getUser().getCpu().getMemory().clear();
|
|
||||||
|
|
||||||
//Write assembled code to mem
|
|
||||||
user.getUser().getCpu().getMemory().write((char) ar.origin, ar.bytes, ar.bytes.length);
|
|
||||||
user.getUser().getCpu().setCodeSegmentOffset(ar.origin);
|
|
||||||
|
|
||||||
JSONObject response = new JSONObject();
|
|
||||||
response.put("t", "codeResponse");
|
|
||||||
response.put("bytes", ar.bytes.length);
|
|
||||||
|
|
||||||
user.getWebSocket().send(response.toJSONString());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
package net.simon987.server.webserver;
|
|
||||||
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
|
|
||||||
public class KeypressHandler implements MessageHandler {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(OnlineUser user, JSONObject json) {
|
|
||||||
|
|
||||||
if(!user.isGuest()) {
|
|
||||||
if(json.get("t").equals("k")){
|
|
||||||
|
|
||||||
LogManager.LOGGER.info("(WS) Received keypress");
|
|
||||||
|
|
||||||
int key = (int)(long)json.get("k");
|
|
||||||
|
|
||||||
user.getUser().getControlledUnit().getKeyboardBuffer().add(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
package net.simon987.server.webserver;
|
|
||||||
|
|
||||||
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
import org.json.simple.parser.JSONParser;
|
|
||||||
import org.json.simple.parser.ParseException;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class MessageEventDispatcher {
|
|
||||||
|
|
||||||
private ArrayList<MessageHandler> handlers = new ArrayList<>(10);
|
|
||||||
|
|
||||||
public MessageEventDispatcher() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addHandler(MessageHandler handler) {
|
|
||||||
|
|
||||||
handlers.add(handler);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispatch(OnlineUser user, String message) {
|
|
||||||
|
|
||||||
JSONParser parser = new JSONParser();
|
|
||||||
try {
|
|
||||||
JSONObject json = (JSONObject) parser.parse(message);
|
|
||||||
|
|
||||||
if (json.containsKey("t")) {
|
|
||||||
for (MessageHandler handler : handlers) {
|
|
||||||
handler.handle(user, json);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LogManager.LOGGER.info("Malformed JSON sent by " + user.getUser().getUsername());
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (ParseException e) {
|
|
||||||
LogManager.LOGGER.info("Malformed JSON sent by " + user.getUser().getUsername());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package net.simon987.server.webserver;
|
|
||||||
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public interface MessageHandler {
|
|
||||||
|
|
||||||
void handle(OnlineUser user, JSONObject json);
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
package net.simon987.server.webserver;
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.game.GameObject;
|
|
||||||
import net.simon987.server.io.JSONSerialisable;
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import org.json.simple.JSONArray;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
public class ObjectsRequestHandler implements MessageHandler {
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(OnlineUser user, JSONObject json) {
|
|
||||||
if (json.get("t").equals("object")) {
|
|
||||||
LogManager.LOGGER.info("(WS) Objects request from " + user.getUser().getUsername());
|
|
||||||
|
|
||||||
|
|
||||||
if (json.containsKey("x") && json.containsKey("y")) {
|
|
||||||
int x = Long.valueOf((long) json.get("x")).intValue();
|
|
||||||
int y = Long.valueOf((long) json.get("y")).intValue();
|
|
||||||
|
|
||||||
ArrayList<GameObject> gameObjects = GameServer.INSTANCE.getGameUniverse().getWorld(x, y).getGameObjects();
|
|
||||||
|
|
||||||
JSONObject response = new JSONObject();
|
|
||||||
JSONArray objects = new JSONArray();
|
|
||||||
|
|
||||||
|
|
||||||
for (GameObject object : gameObjects) {
|
|
||||||
|
|
||||||
if (object instanceof JSONSerialisable) {
|
|
||||||
objects.add(object.serialise());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
response.put("t", "object");
|
|
||||||
response.put("objects", objects);
|
|
||||||
|
|
||||||
|
|
||||||
if (user.getWebSocket().isOpen()) {
|
|
||||||
user.getWebSocket().send(response.toJSONString());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
LogManager.LOGGER.info("(WS) Malformed Objects request from " + user.getUser().getUsername());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,286 +0,0 @@
|
|||||||
package net.simon987.server.webserver;
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.ServerConfiguration;
|
|
||||||
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.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(30);
|
|
||||||
|
|
||||||
|
|
||||||
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());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@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) {
|
|
||||||
|
|
||||||
LogManager.LOGGER.severe("an error occured on connection " + conn.getRemoteSocketAddress() + ':' + ex);
|
|
||||||
userManager.remove(userManager.getUser(conn));
|
|
||||||
conn.close();
|
|
||||||
|
|
||||||
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");
|
|
||||||
|
|
||||||
for (OnlineUser user : userManager.getOnlineUsers()) {
|
|
||||||
|
|
||||||
if (user.getWebSocket().isOpen()) {
|
|
||||||
|
|
||||||
if(user.isGuest()) {
|
|
||||||
|
|
||||||
user.getWebSocket().send(json.toJSONString());
|
|
||||||
|
|
||||||
} else {
|
|
||||||
//Send keyboard updated buffer
|
|
||||||
try{
|
|
||||||
ArrayList<Integer> kbBuffer = user.getUser().getControlledUnit().getKeyboardBuffer();
|
|
||||||
JSONArray keys = new JSONArray();
|
|
||||||
keys.addAll(kbBuffer);
|
|
||||||
json.put("keys", keys);
|
|
||||||
//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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +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.Connection;
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,43 +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")) {
|
|
||||||
|
|
||||||
LogManager.LOGGER.info("Terrain request from " + user.getUser().getUsername());
|
|
||||||
|
|
||||||
World world = GameServer.INSTANCE.getGameUniverse().getWorld(
|
|
||||||
Long.valueOf((long) json.get("x")).intValue(),
|
|
||||||
Long.valueOf((long) json.get("y")).intValue());
|
|
||||||
|
|
||||||
//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("terrain", terrain);
|
|
||||||
|
|
||||||
user.getWebSocket().send(response.toJSONString());
|
|
||||||
} else {
|
|
||||||
LogManager.LOGGER.severe("FIXME handle:TerrainRequestHandler");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package net.simon987.server.webserver;
|
|
||||||
|
|
||||||
import net.simon987.server.GameServer;
|
|
||||||
import net.simon987.server.game.GameObject;
|
|
||||||
import net.simon987.server.logging.LogManager;
|
|
||||||
import org.json.simple.JSONObject;
|
|
||||||
|
|
||||||
public class UserInfoRequestHandler implements MessageHandler {
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(OnlineUser user, JSONObject message) {
|
|
||||||
|
|
||||||
if (message.get("t").equals("userInfo")) {
|
|
||||||
|
|
||||||
LogManager.LOGGER.info("(WS) User info request from " + user.getUser().getUsername());
|
|
||||||
|
|
||||||
if(user.isGuest()) {
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
json.put("t", "userInfo");
|
|
||||||
json.put("worldX", GameServer.INSTANCE.getConfig().getInt("new_user_worldX"));
|
|
||||||
json.put("worldY", GameServer.INSTANCE.getConfig().getInt("new_user_worldY"));
|
|
||||||
|
|
||||||
user.getWebSocket().send(json.toJSONString());
|
|
||||||
} else {
|
|
||||||
GameObject object = (GameObject)user.getUser().getControlledUnit();
|
|
||||||
|
|
||||||
JSONObject json = new JSONObject();
|
|
||||||
json.put("t", "userInfo");
|
|
||||||
json.put("worldX", object.getWorld().getX());
|
|
||||||
json.put("worldY", object.getWorld().getY());
|
|
||||||
|
|
||||||
user.getWebSocket().send(json.toJSONString());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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.io.File;
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class CPUTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void executeInstruction() throws CancelledException {
|
|
||||||
|
|
||||||
ServerConfiguration config = new ServerConfiguration(new File("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();
|
|
||||||
random.nextBytes(cpu.getMemory().getBytes());
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
package net.simon987.server.assembly;
|
|
||||||
|
|
||||||
import net.simon987.server.ServerConfiguration;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
|
|
||||||
public class MemoryTest {
|
|
||||||
@Test
|
|
||||||
public void getSet() {
|
|
||||||
ServerConfiguration config = new ServerConfiguration(new File("config.properties"));
|
|
||||||
int memorySize = config.getInt("memory_size");
|
|
||||||
Memory memory = new Memory(memorySize);
|
|
||||||
|
|
||||||
memory.set(1, 1);
|
|
||||||
assertEquals(1, memory.get(1));
|
|
||||||
|
|
||||||
memory.set(memorySize / 2 - 1, 1);
|
|
||||||
assertEquals(1, memory.get(memorySize / 2 - 1));
|
|
||||||
|
|
||||||
memory.get(memorySize / 2);
|
|
||||||
memory.get(-1);
|
|
||||||
|
|
||||||
memory.set(memorySize / 2, 1);
|
|
||||||
memory.set(-1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void write() {
|
|
||||||
|
|
||||||
ServerConfiguration config = new ServerConfiguration(new File("config.properties"));
|
|
||||||
int memorySize = config.getInt("memory_size");
|
|
||||||
Memory memory = new Memory(memorySize);
|
|
||||||
|
|
||||||
|
|
||||||
assertTrue(memory.write(0, new byte[memorySize], memorySize));
|
|
||||||
assertFalse(memory.write(0, new byte[memorySize], memorySize + 1));
|
|
||||||
assertFalse(memory.write(0, new byte[memorySize], -1));
|
|
||||||
assertFalse(memory.write(-1, new byte[memorySize], 10));
|
|
||||||
|
|
||||||
assertFalse(memory.write(memorySize / 2, new byte[15], 1));
|
|
||||||
assertFalse(memory.write((memorySize / 2) - 5, new byte[11], 11));
|
|
||||||
assertTrue(memory.write((memorySize / 2) - 5, new byte[11], 10));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,47 +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;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class CallInstructionTest {
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void execute() throws Exception {
|
|
||||||
|
|
||||||
ServerConfiguration config = new ServerConfiguration(new File("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 {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
# MySQL username
|
|
||||||
mysql_user=mar
|
|
||||||
# MySQL password/
|
|
||||||
mysql_pass=mar
|
|
||||||
# MySQL address
|
|
||||||
mysql_url=jdbc:mysql://localhost:3306/mar?useSSL=false
|
|
||||||
save_interval=10
|
|
||||||
# Web server port
|
|
||||||
webSocket_port=8887
|
|
||||||
webSocket_host=localhost
|
|
||||||
|
|
||||||
use_secure_webSocket=1
|
|
||||||
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=1024
|
|
||||||
# Address of the stack bottom
|
|
||||||
stack_bottom=32768
|
|
||||||
# Size of the memory in bytes
|
|
||||||
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 yield for a plant
|
|
||||||
plant_yield=2
|
|
||||||
# Grow time in ticks for a plant to grow
|
|
||||||
plant_grow_time=32
|
|
||||||
# Minimum tree count for the WorldGenerator
|
|
||||||
minTreeCount=3
|
|
||||||
# Maximum tree count for the WorldGenerator
|
|
||||||
maxTreeCount=10
|
|
||||||
# ----------------------------------------------
|
|
||||||
# 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 tiles count for the WorldGenerator
|
|
||||||
wg_minIronCount=0
|
|
||||||
# Minimum 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=40
|
|
||||||
27
docker-compose.yml
Normal file
27
docker-compose.yml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
version: "2.1"
|
||||||
|
services:
|
||||||
|
server:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
command: sh -c "/usr/bin/java -jar /app/target/server-1.4a.jar"
|
||||||
|
depends_on:
|
||||||
|
mongodb:
|
||||||
|
condition: service_healthy
|
||||||
|
ports:
|
||||||
|
- 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
|
||||||
Binary file not shown.
Binary file not shown.
155
pom.xml
Normal file
155
pom.xml
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
</project>
|
||||||
94
src/main/java/net/simon987/mar/biomass/BiomassBlob.java
Normal file
94
src/main/java/net/simon987/mar/biomass/BiomassBlob.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/main/java/net/simon987/mar/biomass/ItemBiomass.java
Normal file
36
src/main/java/net/simon987/mar/biomass/ItemBiomass.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/main/java/net/simon987/mar/biomass/WorldUtils.java
Normal file
78
src/main/java/net/simon987/mar/biomass/WorldUtils.java
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package net.simon987.mar.biomass;
|
||||||
|
|
||||||
|
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;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class WorldUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a list of biomass blobs for a world
|
||||||
|
*/
|
||||||
|
public static ArrayList<BiomassBlob> generateBlobs(World world, int minCount, int maxCount, int yield) {
|
||||||
|
|
||||||
|
Random random = new Random();
|
||||||
|
int blobCount = random.nextInt(maxCount - minCount) + minCount;
|
||||||
|
ArrayList<BiomassBlob> biomassBlobs = new ArrayList<>(blobCount);
|
||||||
|
|
||||||
|
//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
|
||||||
|
TileMap m = world.getTileMap();
|
||||||
|
int plainCount = 0;
|
||||||
|
for (int y = 0; y < world.getWorldSize(); y++) {
|
||||||
|
for (int x = 0; x < world.getWorldSize(); x++) {
|
||||||
|
|
||||||
|
if (m.getTileIdAt(x, y) == TilePlain.ID) {
|
||||||
|
plainCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blobCount > plainCount) {
|
||||||
|
blobCount = plainCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
outerLoop:
|
||||||
|
for (int i = 0; i < blobCount; i++) {
|
||||||
|
|
||||||
|
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.getWorldSize() - 1 || p.y == world.getWorldSize() - 1 ||
|
||||||
|
world.getGameObjectsAt(p.x, p.y).size() != 0) {
|
||||||
|
p = m.getRandomTile(TilePlain.ID);
|
||||||
|
counter++;
|
||||||
|
|
||||||
|
if (counter > 25) {
|
||||||
|
continue outerLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (BiomassBlob biomassBlob : biomassBlobs) {
|
||||||
|
if (biomassBlob.getX() == p.x && biomassBlob.getY() == p.y) {
|
||||||
|
//There is already a blob here
|
||||||
|
continue outerLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BiomassBlob biomassBlob = new BiomassBlob();
|
||||||
|
biomassBlob.setObjectId(new ObjectId());
|
||||||
|
biomassBlob.setBiomassCount(yield);
|
||||||
|
biomassBlob.setX(p.x);
|
||||||
|
biomassBlob.setY(p.y);
|
||||||
|
biomassBlob.setWorld(world);
|
||||||
|
|
||||||
|
biomassBlobs.add(biomassBlob);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return biomassBlobs;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package net.simon987.mar.biomass.event;
|
||||||
|
|
||||||
|
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 final int biomassDropCount;
|
||||||
|
|
||||||
|
public ObjectDeathListener(IServerConfiguration config) {
|
||||||
|
biomassDropCount = config.getInt("harvester_biomass_drop_count");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class getListenedEventType() {
|
||||||
|
return ObjectDeathEvent.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(GameEvent event) {
|
||||||
|
|
||||||
|
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() != 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and return a biomass at the given x, y coordinates and in the world
|
||||||
|
* @param x x coord of biomass location
|
||||||
|
* @param y y coord of biomass location
|
||||||
|
* @param world world in which the biomass will be created in
|
||||||
|
* @return the new BiomassBlob created
|
||||||
|
*/
|
||||||
|
private BiomassBlob createBiomassBlobAt(int x, int y, World world) {
|
||||||
|
|
||||||
|
BiomassBlob biomassBlob = new BiomassBlob();
|
||||||
|
biomassBlob.setObjectId(new ObjectId());
|
||||||
|
biomassBlob.setBiomassCount(biomassDropCount);
|
||||||
|
biomassBlob.setX(x);
|
||||||
|
biomassBlob.setY(y);
|
||||||
|
biomassBlob.setWorld(world);
|
||||||
|
|
||||||
|
return biomassBlob;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
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.event.GameEvent;
|
||||||
|
import net.simon987.mar.server.event.GameEventListener;
|
||||||
|
import net.simon987.mar.server.event.WorldGenerationEvent;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class WorldCreationListener implements GameEventListener {
|
||||||
|
@Override
|
||||||
|
public Class getListenedEventType() {
|
||||||
|
return WorldGenerationEvent.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(GameEvent event) {
|
||||||
|
|
||||||
|
int minCount = GameServer.INSTANCE.getConfig().getInt("minBiomassCount");
|
||||||
|
int maxCount = GameServer.INSTANCE.getConfig().getInt("maxBiomassCount");
|
||||||
|
int yield = GameServer.INSTANCE.getConfig().getInt("biomass_yield");
|
||||||
|
|
||||||
|
ArrayList<BiomassBlob> biomassBlobs = WorldUtils.generateBlobs(((WorldGenerationEvent) event).getWorld(),
|
||||||
|
minCount, maxCount, yield);
|
||||||
|
|
||||||
|
for (BiomassBlob blob : biomassBlobs) {
|
||||||
|
((WorldGenerationEvent) event).getWorld().addObject(blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
111
src/main/java/net/simon987/mar/construction/BluePrint.java
Normal file
111
src/main/java/net/simon987/mar/construction/BluePrint.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
104
src/main/java/net/simon987/mar/construction/Obstacle.java
Normal file
104
src/main/java/net/simon987/mar/construction/Obstacle.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/main/java/net/simon987/mar/cubot/Clock.java
Normal file
52
src/main/java/net/simon987/mar/cubot/Clock.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
610
src/main/java/net/simon987/mar/cubot/Cubot.java
Normal file
610
src/main/java/net/simon987/mar/cubot/Cubot.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
120
src/main/java/net/simon987/mar/cubot/CubotBattery.java
Normal file
120
src/main/java/net/simon987/mar/cubot/CubotBattery.java
Normal 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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
130
src/main/java/net/simon987/mar/cubot/CubotComPort.java
Normal file
130
src/main/java/net/simon987/mar/cubot/CubotComPort.java
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package net.simon987.mar.cubot;
|
||||||
|
|
||||||
|
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 HardwareModule {
|
||||||
|
|
||||||
|
public static final char HWID = 0xD;
|
||||||
|
public static final int DEFAULT_ADDRESS = 0xD;
|
||||||
|
|
||||||
|
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(ControllableUnit unit) {
|
||||||
|
super(null, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CubotComPort(Document document, ControllableUnit cubot) {
|
||||||
|
super(document, cubot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int MESSAGE_LENGTH = 8;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleInterrupt(Status status) {
|
||||||
|
|
||||||
|
int a = getCpu().getRegisterSet().getRegister("A").getValue();
|
||||||
|
|
||||||
|
if (a == COMPORT_BUFFER_CLEAR) {
|
||||||
|
|
||||||
|
unit.getConsoleMessagesBuffer().clear();
|
||||||
|
|
||||||
|
} else if (a == COMPORT_CONSOLE_CLEAR) {
|
||||||
|
|
||||||
|
if (unit instanceof Cubot) {
|
||||||
|
((Cubot) unit).setConsoleMode(Cubot.ConsoleMode.CLEAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (a == COMPORT_POLL) {
|
||||||
|
|
||||||
|
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 : unit.getConsoleMessagesBuffer()) {
|
||||||
|
if (x + MESSAGE_LENGTH >= getCpu().getMemory().getWords().length) {
|
||||||
|
//todo set interrupt ?
|
||||||
|
getCpu().getStatus().setErrorFlag(true);
|
||||||
|
} else {
|
||||||
|
System.arraycopy(message, 0, getCpu().getMemory().getWords(), x, MESSAGE_LENGTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set B = number of messages
|
||||||
|
getCpu().getRegisterSet().getRegister("B").setValue(unit.getConsoleMessagesBuffer().size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (a == COMPORT_FRONT_PORT_OUT) {
|
||||||
|
|
||||||
|
if (unit.spendEnergy(5)) {
|
||||||
|
//Get object directly in front of the Cubot
|
||||||
|
Point frontTile = unit.getFrontTile();
|
||||||
|
//Todo will have to add getGameObjectsBlockingAt to enable Factory
|
||||||
|
ArrayList<GameObject> objects = unit.getWorld().getGameObjectsAt(frontTile.x, frontTile.y);
|
||||||
|
|
||||||
|
if (objects.size() > 0 && objects.get(0) instanceof MessageReceiver) {
|
||||||
|
|
||||||
|
int x = getCpu().getRegisterSet().getRegister("X").getValue();
|
||||||
|
|
||||||
|
if (x + MESSAGE_LENGTH >= getCpu().getMemory().getWords().length) {
|
||||||
|
//todo set interrupt ?
|
||||||
|
getCpu().getStatus().setErrorFlag(true);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
//Get MESSAGE_LENGTH-word message pointed by X
|
||||||
|
char[] message = new char[MESSAGE_LENGTH];
|
||||||
|
System.arraycopy(getCpu().getMemory().getWords(), x, message, 0, MESSAGE_LENGTH);
|
||||||
|
|
||||||
|
//Send it to the MessageReceiver object
|
||||||
|
getCpu().getRegisterSet().getRegister("B").setValue(
|
||||||
|
((MessageReceiver) objects.get(0)).sendMessage(message) ? 1 : 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCpu().getRegisterSet().getRegister("B").setValue(0); //Failed
|
||||||
|
|
||||||
|
} else if (a == COMPORT_SELF_OUT) {
|
||||||
|
|
||||||
|
if (unit.spendEnergy(1)) {
|
||||||
|
|
||||||
|
int x = getCpu().getRegisterSet().getRegister("X").getValue();
|
||||||
|
|
||||||
|
//Write a single message to console buffer
|
||||||
|
if (x + MESSAGE_LENGTH >= getCpu().getMemory().getWords().length) {
|
||||||
|
//todo set interrupt ?
|
||||||
|
getCpu().getStatus().setErrorFlag(true);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
//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(unit.sendMessage(message) ? 1 : 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getCpu().getRegisterSet().getRegister("B").setValue(0); //Failed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char getId() {
|
||||||
|
return HWID;
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/main/java/net/simon987/mar/cubot/CubotCore.java
Normal file
46
src/main/java/net/simon987/mar/cubot/CubotCore.java
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package net.simon987.mar.cubot;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class CubotCore extends HardwareModule {
|
||||||
|
|
||||||
|
public static final int DEFAULT_ADDRESS = 0x000E;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hardware ID (Should be unique)
|
||||||
|
*/
|
||||||
|
public static final char HWID = 0x000E;
|
||||||
|
|
||||||
|
private static final int CORE_STATUS_POLL = 1;
|
||||||
|
private static final int CORE_HULL_POLL = 2;
|
||||||
|
|
||||||
|
public CubotCore(ControllableUnit unit) {
|
||||||
|
super(null, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CubotCore(Document document, ControllableUnit unit) {
|
||||||
|
super(document, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleInterrupt(Status status) {
|
||||||
|
|
||||||
|
int a = getCpu().getRegisterSet().getRegister("A").getValue();
|
||||||
|
|
||||||
|
if (a == CORE_STATUS_POLL) {
|
||||||
|
if (unit instanceof Cubot) {
|
||||||
|
getCpu().getRegisterSet().getRegister("B").setValue(((Cubot) unit).getStatus());
|
||||||
|
}
|
||||||
|
} else if (a == CORE_HULL_POLL) {
|
||||||
|
getCpu().getRegisterSet().getRegister("B").setValue(unit.getHp());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char getId() {
|
||||||
|
return HWID;
|
||||||
|
}
|
||||||
|
}
|
||||||
60
src/main/java/net/simon987/mar/cubot/CubotDrill.java
Normal file
60
src/main/java/net/simon987/mar/cubot/CubotDrill.java
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package net.simon987.mar.cubot;
|
||||||
|
|
||||||
|
import net.simon987.mar.server.assembly.HardwareModule;
|
||||||
|
import net.simon987.mar.server.assembly.Status;
|
||||||
|
import net.simon987.mar.server.game.item.Item;
|
||||||
|
import net.simon987.mar.server.game.objects.Action;
|
||||||
|
import net.simon987.mar.server.game.objects.ControllableUnit;
|
||||||
|
import net.simon987.mar.server.game.world.Tile;
|
||||||
|
import org.bson.Document;
|
||||||
|
|
||||||
|
public class CubotDrill extends HardwareModule {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
|
||||||
|
public CubotDrill(ControllableUnit unit) {
|
||||||
|
super(null, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CubotDrill(Document document, ControllableUnit cubot) {
|
||||||
|
super(document, 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 (unit.spendEnergy(1400)) {
|
||||||
|
if (unit.getCurrentAction() == Action.IDLE) {
|
||||||
|
|
||||||
|
Tile tile = unit.getWorld().getTileMap().getTileAt(unit.getX(), unit.getY());
|
||||||
|
|
||||||
|
Item newItem = tile.drill();
|
||||||
|
if (newItem != null) {
|
||||||
|
unit.setCurrentAction(Action.DIGGING);
|
||||||
|
unit.giveItem(newItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user