Commits vergleichen

...

294 Commits

Autor SHA1 Nachricht Datum
c702acb9d8 Merge pull request 'Update EventFight.hasFinished' (#69) from UpdateEventFight into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #69
Reviewed-by: Lixfel <lixfel@steamwar.de>
2024-08-03 15:35:08 +02:00
yoyosource
14bea165c5 Update EventFight.hasFinished
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2024-08-03 15:29:33 +02:00
Lixfel
d000b8687d Fix logGetId
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <git-5w3l@lixfel.de>
2024-07-06 15:12:16 +02:00
Lixfel
1237a699ba Fix SWCommand with 0 args, Add SWException option to get ID
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <git-5w3l@lixfel.de>
2024-07-05 10:07:22 +02:00
Lixfel
e76473e0be Fix SWCommand with 0 args
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <git-5w3l@lixfel.de>
2024-07-03 22:17:39 +02:00
Lixfel
0caf885959 Add TICKET_LOG, Update dependencies
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <git-5w3l@lixfel.de>
2024-07-03 22:05:46 +02:00
20c22c47f9 Merge pull request 'Kleiner Fehler' (#68) from chaoscaot-patch-1 into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #68
Reviewed-by: Lixfel <lixfel@steamwar.de>
2024-03-16 00:45:27 +01:00
0c35be1fec Kleiner Fehler
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2024-03-15 22:40:53 +01:00
Chaoscaot
0bc349fc64 Kleiner Fehler
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2024-03-15 22:39:41 +01:00
a473e59048 Merge pull request 'Configurable Spectate port to allow multiple concurrent Spectate servers' (#66) from spectate into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #66
Reviewed-by: YoyoNow <jwsteam@nidido.de>
2024-03-13 13:11:38 +01:00
3c889efe9e Configurable Spectate port to allow multiple concurrent Spectate servers
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2024-03-09 21:22:24 +01:00
98c804a15c Merge pull request '"UNION with CTE" Workaround' (#65) from fix-union into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #65
Reviewed-by: Lixfel <lixfel@steamwar.de>
2024-03-09 19:36:22 +01:00
d5dbe05e29 "UNION with CTE" Workaround
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2024-03-09 18:28:49 +01:00
647012ac40 Merge pull request 'Add referee table for multiple referees per event(fight)' (#64) from referee into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #64
Reviewed-by: YoyoNow <jwsteam@nidido.de>
2024-03-08 10:16:46 +01:00
7ccabe8dcc Add referee table for multiple referees per event(fight)
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2024-03-08 09:52:53 +01:00
bfc72ddcb7 Merge pull request 'Fix SchematicSelector' (#63) from fix-schem-select into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #63
Reviewed-by: YoyoNow <jwsteam@nidido.de>
2024-02-17 08:29:08 +01:00
fe471aa803 Fix SchematicSelector
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2024-02-03 10:29:34 +01:00
41fc368214 Revert "Fix Invalid Show"
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
This reverts commit 38939b36bd.
2024-02-03 10:28:32 +01:00
38939b36bd Fix Invalid Show
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2024-02-03 10:28:07 +01:00
e7952685d2 Merge pull request 'Add PWs' (#61) from passwords into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #61
Reviewed-by: Lixfel <lixfel@steamwar.de>
2024-01-06 16:32:44 +01:00
9a3630b07c Code Review Stuff
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-12-19 20:49:37 +01:00
61ffbc2349 Merge pull request 'Add BauweltMember.isBuild and BauweltMember.isSupervisor' (#62) from BauweltMemberUpdate into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #62
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-12-19 20:49:24 +01:00
efd7f27c60 Unused Imports and Random
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-12-19 20:39:09 +01:00
yoyosource
abb6af2d7d Add BauweltMember.isBuild and BauweltMember.isSupervisor
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-12-19 20:10:28 +01:00
yoyosource
f672b8557b Add BauweltMember.isBuild and BauweltMember.isSupervisor
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-12-19 20:08:39 +01:00
c20a426bff Merge remote-tracking branch 'origin/passwords' into passwords
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
# Conflicts:
#	src/de/steamwar/sql/SteamwarUser.java
2023-12-17 18:29:20 +01:00
e83ab4762d WepPW Command 2023-12-17 18:28:52 +01:00
Chaoscaot
6ac6643162 src/de/steamwar/sql/SteamwarUser.java aktualisiert
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-12-17 17:22:47 +01:00
ea52162ffe Updates
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-12-17 16:34:08 +01:00
b66c18b8b3 Merge pull request 'Add Token SQL-Class' (#60) from token into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #60
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-12-11 18:18:00 +01:00
0723c87814 Add Token
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-11-30 18:11:39 +01:00
e664c6cf4e Merge pull request 'Fix QuickGear' (#59) from fix-quickgear into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #59
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-11-29 16:31:46 +01:00
ac7c3acbb8 Fix QuickGear
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-11-29 16:27:20 +01:00
e4b3539d28 Merge pull request 'Remove UserGroup' (#58) from removeUserGroups into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #58
Reviewed-by: Chaoscaot <chaoscaot@zohomail.eu>
2023-10-10 18:00:58 +02:00
e0c1d1c2e2 Change Colors
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-09-01 11:25:00 +02:00
eaad69240a Remove UserGroup
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-08-30 12:27:26 +02:00
72ebfec1a5 Fix UserPerm
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-08-29 21:40:52 +02:00
4c008bc282 Merge pull request 'UserPerm model, lazy punishment loading' (#57) from user-perm into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #57
Reviewed-by: Chaoscaot <chaoscaot@zohomail.eu>
2023-08-29 21:24:48 +02:00
4eadcb4b09 Update ColorChat Perm
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-08-29 18:05:26 +02:00
fc1e7c60d7 Updated UserPerms
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-08-29 17:46:46 +02:00
96d55a7ac6 Changed UserPerms
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-08-29 16:01:13 +02:00
469a504e48 UserPerm model
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-08-29 15:03:39 +02:00
0fef6c0f8f Fix script insert
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-07-17 16:52:51 +02:00
Chaoscaot
5d5adb9828
Fix SQL
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-07-17 16:47:42 +02:00
6c7c073320 Merge pull request 'Fix infinite recursion' (#56) from improve_sql into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #56
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-06-13 23:15:20 +02:00
Chaoscaot
7f1cf130a8 Fix infinite recursion
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-06-13 22:59:56 +02:00
e903c139a7 Merge pull request 'Add Script SQL-Type' (#54) from script_sql into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #54
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-06-13 22:07:53 +02:00
6f5ac96c65 Merge pull request 'Improve Fightsystem SchematicSelector Performance' (#55) from improve_sql into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #55
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-06-10 17:54:31 +02:00
Chaoscaot
18e3f9baae Fix Query
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-06-10 11:04:45 +02:00
Chaoscaot
9eef3c9353 Improve Fightsystem SchematicSelector Performance
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-06-08 11:11:05 +02:00
d6ef17cbe7 Fix Script class
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-06-05 16:01:34 +02:00
Chaoscaot
f07ad1cbf5 Add List
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-18 14:23:44 +02:00
Chaoscaot
eedc949672 Add List
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-18 14:23:23 +02:00
Chaoscaot
b4ce9493d1 Add Script SQL-Type
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-18 08:43:33 +02:00
yoyosource
a2bb52ab26 Fix another thing
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-13 16:00:54 +02:00
yoyosource
5ca6d9f4a4 Update to use Collections.emptyMap()
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-12 23:40:05 +02:00
yoyosource
92f32e410b Update some more stuff
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-12 23:36:20 +02:00
e3e1a1cfcd Merge pull request 'Update UserElo for new ranked system' (#53) from NewRankedSystem into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #53
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-05-12 22:54:19 +02:00
yoyosource
a8270a386c Add UserElo.ELO_DEFAULT
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-12 17:25:06 +02:00
yoyosource
727a27a9fb Update UserElo for new ranked system
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-11 16:05:21 +02:00
bf6f6b920d Merge pull request 'Add duration field to FightEndsPacket' (#52) from DurationFieldInFightEndsPacket into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #52
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-05-11 09:08:00 +02:00
9df7ed39a4 Merge branch 'master' into DurationFieldInFightEndsPacket
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-10 17:07:12 +02:00
yoyosource
0b274f288f Add duration field to FightEndsPacket
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-10 17:06:28 +02:00
a26e69e6f2 Fix SchemDownload
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-08 16:31:11 +02:00
97c38b64b3 Fix loading order
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-03 17:52:21 +02:00
229f74991e Fix loading order
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-03 17:50:27 +02:00
59e397b826 Better error handling
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-03 17:47:10 +02:00
yoyosource
5aca43b35e Hotfix Season.getSeasonStart
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-01 11:33:09 +02:00
yoyosource
4a6465953f Hotfix Season.getSeasonStart
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-05-01 11:19:00 +02:00
e8d964d833 Merge pull request 'Add feature disable' (#49) from LinkageFeatureDisable into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #49
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-04-11 11:18:53 +02:00
fde6bda521 Merge pull request 'Add RequestSchematicSearchPacket' (#50) from schemsearch_request into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #50
Reviewed-by: YoyoNow <jwsteam@nidido.de>
2023-04-11 11:09:35 +02:00
Chaoscaot
e4c2b05e4f Add RequestSchematicSearchPacket
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-04-07 22:28:44 +02:00
yoyosource
595df40e1e Hotfix tab completions for help string varargs commands
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-03-17 21:09:08 +01:00
yoyosource
41b46eb6f5 Add initialize call with parent for part of
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-03-14 16:57:11 +01:00
yoyosource
780caae80d Add initialize call with parent for part of
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-03-14 16:53:17 +01:00
yoyosource
d06970f494 Add initialize call with parent for part of
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-03-14 16:49:51 +01:00
yoyosource
7474478489 Hotfix help system
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-03-05 21:58:52 +01:00
yoyosource
6f7846956e Hotfix help system
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-03-05 21:54:35 +01:00
yoyosource
92cddbada1 Hotfix the hotfix for Validator
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-03-04 12:36:06 +01:00
yoyosource
b48a25453d Hotfix the hotfix for Validator
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-03-04 12:35:44 +01:00
yoyosource
53efec2d0f Hotfix invert on first parameter
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-03-04 12:24:46 +01:00
yoyosource
32de0b912d Hotfix invert from Validator
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-03-04 12:08:20 +01:00
yoyosource
362eb8b6bb Add feature disable
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-03-03 20:37:37 +01:00
36bb4db515 Merge pull request 'LinkageOptimizations' (#48) from LinkageOptimizations into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #48
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-03-01 20:07:47 +01:00
e36022e64f Fix MemberCache invalidation
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-02-26 15:50:26 +01:00
yoyosource
4f4caac67f Fix LinkageProcessor
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-25 15:04:30 +01:00
yoyosource
567187ea88 Merge branch 'master' into LinkageOptimizations
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-25 14:34:54 +01:00
3a8df67f19 Fix banneduserips
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-02-25 07:35:32 +01:00
ec9d56dab9 Hotfix NPE
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-02-24 17:08:18 +01:00
38457db014 Hotfix NPE
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-02-24 13:36:19 +01:00
fe09afdb34 Merge pull request 'Seperate SchematicData' (#45) from seperate_schemdata into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #45
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-02-24 10:32:21 +01:00
Chaoscaot
aa91584a1e Add BIS
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-24 10:24:29 +01:00
Chaoscaot
178c96e7c6 Fix Queries
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-24 09:31:03 +01:00
Chaoscaot
9c15c0ccfe Fix Queries
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-24 09:21:50 +01:00
Chaoscaot
f2f2a901e5 Fix Queries
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-24 09:19:28 +01:00
Chaoscaot
2ff1ebea25 Fix Queries
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-24 07:30:55 +01:00
Chaoscaot
9c6a7cf07d Add License Header
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-23 18:19:18 +01:00
Chaoscaot
d22bb36fc3 Merge branch 'master' into seperate_schemdata
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-23 17:31:26 +01:00
3778d6c1f9 Hotfix SWUser Init
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-23 17:20:21 +01:00
95b46f3a9a Fix Team lookup
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-02-23 14:03:51 +01:00
74cf3dd7c2 Merge pull request 'Fix SchematicNode Tabcomplete' (#47) from fix_tabcomplete into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #47
Reviewed-by: YoyoNow <jwsteam@nidido.de>
2023-02-23 10:18:02 +01:00
Chaoscaot
109cc3ec7e Fix SchematicNode Tabcomplete
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-23 09:55:59 +01:00
b2b96cff04 Merge pull request 'CommonDB' (#46) from commonDB2 into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #46
Reviewed-by: YoyoNow <jwsteam@nidido.de>
2023-02-23 08:05:56 +01:00
yoyosource
615f46dfe7 Merge branch 'master' into LinkageOptimizations
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-22 18:50:07 +01:00
588d3c9dda Fix Bugs
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-02-22 16:18:03 +01:00
9b53ad748b Es ist vollbracht
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-02-21 20:03:17 +01:00
8dbf62e560 WIP CommonDB: Milestone SWUser
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-02-21 18:22:06 +01:00
fbb07b1733 Merge branch 'master' into commonDB2 2023-02-21 15:58:35 +01:00
Chaoscaot
e52e9c5ccd Seperate SchematicData
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-21 13:24:32 +01:00
aa70f423d8 Merge pull request 'Add PartOf annotation for splitting big commands into multiple files' (#44) from CMDAPIPartOf into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #44
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-02-18 14:01:34 +01:00
yoyosource
69cff99801 Make Map final
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-18 14:01:14 +01:00
yoyosource
241117a6a6 Fix test
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-18 12:13:20 +01:00
yoyosource
5d53ede5e8 Add PartOf annotation for splitting big commands into multiple files
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-18 12:05:03 +01:00
3e1c4e9059 WIP CommonDB
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-02-17 11:47:39 +01:00
d439ecb8d0 WIP CommonDB
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-02-04 12:54:50 +01:00
fca45b084e Merge branch 'master' into commonDB2 2023-02-04 12:52:53 +01:00
89b0c14da6 Merge pull request 'CMDAPIVarArgsSorting' (#43) from CMDAPIVarArgsSorting into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #43
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-02-04 11:54:24 +01:00
yoyosource
45e9698634 Fix command sorting with varargs
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-02 18:07:29 +01:00
yoyosource
65df8ddab0 Fix command sorting with varargs
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-02-02 17:53:56 +01:00
yoyosource
ef79a2e7db Add some unfinished new sorting
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-25 20:41:16 +01:00
e2654e72bf WIP CommonDB 2
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-01-23 16:30:35 +01:00
69024c3bb9 Merge pull request 'CMDAPIArrayLength' (#41) from CMDAPIArrayLength into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #41
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-01-23 16:13:54 +01:00
yoyosource
49e4bf64b7 Add ArrayLength test
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-23 14:06:40 +01:00
yoyosource
4e6f73c637 Add ArrayLength visual update for tabcompletions
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-22 22:05:53 +01:00
a560ddd689 Merge pull request 'CMDAPIChangesForTeamCommand' (#40) from CMDAPIChangesForTeamCommand into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #40
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-01-22 19:10:57 +01:00
yoyosource
e31534632e Fix stuff and update tests
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-22 16:04:35 +01:00
yoyosource
c9d915b01e Fix stuff and update tests
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-22 15:41:05 +01:00
yoyosource
6cbee16c19 Remove Quotable because it was a dumb idea to add
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Add AbstractSWCommand.Length and AbstractSWCommand.ArrayLength
2023-01-22 13:45:22 +01:00
yoyosource
2cbd7b8117 Add inverting of validator, removes more boilerplate
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-21 22:12:56 +01:00
yoyosource
b4df616bc4 Fix some tab completion cache stuff
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-21 21:46:48 +01:00
61999b0507 Merge pull request 'CMDAPIBetterCaching' (#39) from CMDAPIBetterCaching into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #39
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-01-21 17:38:32 +01:00
yoyosource
8100f9c044 Retain current behaviour
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-21 17:07:48 +01:00
yoyosource
9992dfaf18 Add better caching key normalization 2023-01-21 17:06:56 +01:00
9ea02ebcd4 Merge pull request 'Fix: Multiple //copy Schematics' (#38) from nodemember into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #38
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-01-21 01:23:15 +01:00
Chaoscaot
8534a01ff2 Merge branch 'master' into nodemember
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-20 23:02:49 +01:00
Chaoscaot
a738dfa1c5 Fix: Multiple //copy Schematics
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-20 19:38:51 +01:00
d12fbbd339 Potential SQL connection churn fix
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2023-01-20 11:08:31 +01:00
5cde375310 Merge pull request 'Fix: Wrong Owner in Breadcrumbs' (#37) from nodemember into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #37
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-01-17 23:02:43 +01:00
Chaoscaot
58d6506a44 Fix: Wrong Owner in Breadcrumbs
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-17 22:52:47 +01:00
900baca296 Merge pull request 'nodemember' (#36) from nodemember into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #36
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-01-17 22:15:50 +01:00
Chaoscaot
ae517a4fb5 Merge branch 'master' into nodemember
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
# Conflicts:
#	src/de/steamwar/sql/SchematicNode.java
2023-01-17 22:10:12 +01:00
Chaoscaot
04577cc076 Fix: Performance of byIdAndUser
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-17 22:08:51 +01:00
ee6412c34e HOTFIX
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-17 21:59:04 +01:00
1a8808fecd HOTFIX
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-17 21:48:59 +01:00
482cbb001f Merge pull request 'Fix: Not Own Schematics not schowing' (#35) from nodemember into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #35
2023-01-17 21:44:21 +01:00
Chaoscaot
47cf5d9e91 Fix: Not Own Schematics not schowing
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-17 21:42:57 +01:00
0ee8d3808c Merge pull request 'Fix: Optimize getPath and fix generateBreadCrumbs' (#34) from nodemember into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #34
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-01-17 21:12:35 +01:00
Chaoscaot
e880c2944b Fix: Optimize getPath and fix generateBreadCrumbs
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-17 21:12:11 +01:00
397cc489e6 Merge pull request 'Fix: Optimize getPath and fix generateBreadCrumbs' (#33) from nodemember into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #33
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-01-17 21:08:49 +01:00
Chaoscaot
d959d8426b Fix: Optimize getPath and fix generateBreadCrumbs
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-17 21:06:51 +01:00
aaac56754c Merge pull request 'Fix: Optimize generateBreadcrumbs' (#32) from nodemember into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #32
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-01-17 20:52:19 +01:00
Chaoscaot
b6be12db92 Fix: Optimize generateBreadcrumbs
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-17 20:49:46 +01:00
1a5455af93 HOTFIX
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-17 20:00:37 +01:00
4e65bfe098 Fix ordering
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-17 19:59:49 +01:00
92c246f319 Merge pull request 'nodemember' (#31) from nodemember into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #31
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-01-17 19:56:54 +01:00
Chaoscaot
ef26175fc3 NodeMember Fix #1
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-17 19:47:47 +01:00
Chaoscaot
73a6e80c49 Merge remote-tracking branch 'origin/master'
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-17 19:38:57 +01:00
Chaoscaot
d6b3dbc446 NodeMember Fix #1 2023-01-17 19:38:50 +01:00
ee35d65992 Revert to null unsafe default
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-17 19:26:19 +01:00
yoyosource
1cf75f3742 Hotfix AbstractSWCommand
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-17 18:27:29 +01:00
4422c7c0ba Hotfix statement
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-17 18:20:24 +01:00
8b8e6a9e57 Merge pull request 'NodeMember: CommenCore' (#27) from nodemember into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #27
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-01-17 18:01:03 +01:00
Chaoscaot
785f31a728 Fixes
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
"0 wird nicht benutzt"
2023-01-16 16:40:01 +01:00
Chaoscaot
d30650bad3 Fixes
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
"0 wird nicht benutzt"
2023-01-16 16:33:17 +01:00
Chaoscaot
27d6e9accf Merge remote-tracking branch 'origin/nodemember' into nodemember
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-15 21:10:39 +01:00
Chaoscaot
2e39d677d2 Add Copyright 2023-01-15 21:10:35 +01:00
Chaoscaot
bd5e73e225 Merge branch 'master' into nodemember
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-15 20:46:29 +01:00
Chaoscaot
a3f13737e4 Add Copyright
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-15 20:46:11 +01:00
yoyosource
d56150a8b1 Fix one weird behaviour
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-15 20:45:29 +01:00
Chaoscaot
ad43512d30 Add Copyright
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-15 20:18:53 +01:00
39e4553f6e Merge pull request 'CMDAPIRework' (#25) from CMDAPIRework into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #25
Reviewed-by: Lixfel <lixfel@steamwar.de>
2023-01-15 20:13:39 +01:00
Chaoscaot
6a6483235c Add Copyright 2023-01-15 20:11:41 +01:00
9276f79d93 Utilize null safe equals 2023-01-15 20:07:01 +01:00
yoyosource
9cc6004271 Merge remote-tracking branch 'origin/CMDAPIRework' into CMDAPIRework 2023-01-15 18:29:25 +01:00
yoyosource
d23f2563f6 Add test for new system 2023-01-15 18:29:14 +01:00
Chaoscaot
c94ee5c55f Add Copyright 2023-01-15 17:55:17 +01:00
eaeb810062 Merge branch 'master' into CMDAPIRework 2023-01-15 17:23:51 +01:00
ef635b055c Merge pull request 'Fix closing connection on invalid connection' (#28) from fix-sql-restart into master
Reviewed-on: #28
Reviewed-by: YoyoNow <jwsteam@nidido.de>
2023-01-15 16:45:40 +01:00
yoyosource
7e67f74571 Add PreviousArguments for better handling in AbstractTypeMapper 2023-01-15 16:40:39 +01:00
dbd91bf41a Fix closing connection on invalid connection
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-15 12:34:18 +01:00
bf1dde08c2 Fix build.gradle
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-15 12:30:57 +01:00
d62dd7614a Adjust CI config to new CI
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-15 12:30:38 +01:00
yoyosource
bd7635da0d Add CommandMetaData.ImplicitValidator for easier extension of implicit validators
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2023-01-15 11:23:58 +01:00
Chaoscaot
45bc5b05d7 Add Copyright
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-14 08:57:04 +01:00
Chaoscaot
8ce900db31 NodeMember: Move Member Schematics
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2023-01-12 21:53:28 +01:00
Chaoscaot
bf480f6e6f Revert "Initial Commit"
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
This reverts commit 6d579b6981.
2022-12-23 23:17:13 +01:00
Chaoscaot
6d579b6981 Initial Commit
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-12-23 23:14:52 +01:00
yoyosource
518207f343 Optimize some code
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-12-18 21:49:36 +01:00
yoyosource
2e9e4e2e49 Fix ErrorMessage annotation and ClassValidator never being present on Parameter
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-12-18 21:36:46 +01:00
yoyosource
19e4949048 Add MetaData to bundle metadata annotations
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-12-18 18:37:10 +01:00
yoyosource
f2c0a2a16b Add MethodMetaData and ParameterMetaData for better type safety
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-12-18 18:14:44 +01:00
yoyosource
4b8310d4f8 Add ApplicableTypes for better type safety
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Add AbstractSWCommand.Min
Add AbstractSWCommand.Max
2022-12-18 17:14:01 +01:00
yoyosource
50f4fd423d Merge remote-tracking branch 'origin/master'
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-12-18 13:55:50 +01:00
yoyosource
2ae13e3642 Add Quotable strings 2022-12-18 13:55:11 +01:00
yoyosource
d38ddb54c7 Merge branch 'master' into CMDAPIStrings
# Conflicts:
#	src/de/steamwar/command/SWCommandUtils.java
2022-12-18 13:32:36 +01:00
yoyosource
8d1b5c5f66 Update number type mapper 2022-12-18 13:31:33 +01:00
c6da22f0be Merge pull request 'Fix Schematic Rename' (#24) from fix_schematic_rename into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #24
2022-12-14 21:55:32 +01:00
Chaoscaot
6fe09a9579 Fix
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-12-14 21:53:52 +01:00
Chaoscaot
fb9abf4c9c Fix
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-12-14 21:52:08 +01:00
Chaoscaot
136dc4a021 Fix
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-12-14 21:50:09 +01:00
Chaoscaot
eb41602253 Fix
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-12-14 21:47:13 +01:00
yoyosource
441661a8f5 Hotfix LinkageProcessor with generics
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-12-07 22:33:37 +01:00
746ae701d8 Fix Event SchemType
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-11-30 14:22:50 +01:00
4706c1525c Fix NodeDownload
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-11-23 21:02:49 +01:00
2dcddd9cc2 Fix NodeDownload
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-11-23 20:33:04 +01:00
bb601245f9 Fix Punishment userid column
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-11-22 19:37:58 +01:00
4b97753167 Fix SchemElo NPE
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-11-22 14:52:06 +01:00
68bb3f3b8b Merge pull request 'commonDb' (#21) from commonDb into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #21
2022-11-22 11:34:03 +01:00
e9a39d007d Current state
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-11-15 18:40:48 +01:00
yoyosource
d4f9246f68 Update some stuff to strings 2022-11-12 15:19:36 +01:00
bd626bb4e6 Current state 2022-11-02 22:38:21 +01:00
92bea6255f Fixes
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-10-29 13:19:36 +02:00
d6213acab0 Fix merge issues
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-10-29 12:42:41 +02:00
yoyosource
30299be974 Optimize some more stuff
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-26 16:40:42 +02:00
yoyosource
860d082770 Simplify LinkageProcessor
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-26 15:58:06 +02:00
3da8d04f35 Merge branch 'master' into commonDb
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-26 10:05:15 +02:00
yoyosource
0c68dfd19d Optimize generated java code
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Fix eager instantiation with special other annotation
2022-09-25 16:54:16 +02:00
yoyosource
bc7ae5cf9a Hotfix spigot listeners
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-25 13:08:14 +02:00
yoyosource
3da72831fe Hotfix package name
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-25 12:04:17 +02:00
yoyosource
69c8ac3861 Hotfix Plain_GENERIC generating invalid java code
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Hotfix weird generation of Plain method in LinkageProcessor
Add Plain for eager instantiation if using custom linkages not generating link or unlink method
2022-09-25 11:55:45 +02:00
c9b4dad9b9 Merge branch 'master' into commonDb 2022-09-24 19:59:54 +02:00
d90938eff2 Merge pull request 'Linkage' (#22) from Linkage into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #22
2022-09-24 19:44:12 +02:00
yoyosource
e02930f124 Add PacketHandler_GENERIC
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-24 13:48:40 +02:00
yoyosource
f0151f2c8d Update some stuff
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-24 13:38:00 +02:00
yoyosource
49a6a6f23f Update some stuff
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-24 13:34:10 +02:00
yoyosource
75ff0c3d45 Update some stuff
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2022-09-24 12:53:18 +02:00
yoyosource
be2b368802 Update MaxVersion check to be inclusive
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-24 11:22:22 +02:00
a249d66dcf Merge pull request 'Linkage' (#20) from Linkage into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #20
2022-09-23 12:45:21 +02:00
yoyosource
b7e5de4894 Remove Writer.java
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Update Bungee/Spigot detection
2022-09-23 12:07:22 +02:00
yoyosource
de02138e8d Add plugin.yml discovery by traversal
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-23 11:49:27 +02:00
yoyosource
4dd552d08d Fix a build error
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-23 09:20:39 +02:00
yoyosource
fd0af779bc Fix LinkageProcessor
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-23 09:05:15 +02:00
yoyosource
a25ea3878d Fix BuildPlan
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-23 09:00:22 +02:00
yoyosource
9eb18d9626 Fix run method generation
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-23 08:57:11 +02:00
yoyosource
33133ed079 Update if generation
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-22 21:48:26 +02:00
yoyosource
8b7c1e08fd Add PluginCheck
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-22 19:20:16 +02:00
yoyosource
eac3433ed1 Add MaxVersion
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-22 19:08:17 +02:00
yoyosource
ff8ffbf5fb Add AllowedContexts
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Add DiscordMode
Add EventMode
2022-09-22 15:21:04 +02:00
yoyosource
f161379b62 Finalize min version checks
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-22 14:53:42 +02:00
yoyosource
067acf5f62 Finalize Linkage
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-22 13:46:39 +02:00
yoyosource
bbd4eeacd3 Fix LinkageType
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-22 13:40:48 +02:00
yoyosource
a2c2b3ecf1 Fix registering target for listeners and for command messages
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2022-09-22 13:39:58 +02:00
yoyosource
f628bb67dd Fix LinkageProcessor
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-22 13:15:22 +02:00
yoyosource
7caa627c93 Update LinkageProcessor
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-22 13:12:51 +02:00
yoyosource
7c21034d33 Update ListenerLink
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-22 12:22:44 +02:00
yoyosource
a58dd0698f Fix initializer methods
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-22 12:21:30 +02:00
yoyosource
05f5d3f7b3 Fix LinkageProcessor
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-22 12:19:45 +02:00
yoyosource
1c7e9ad039 Update linkage api
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-22 11:52:09 +02:00
9043ab9dc5 Merge branch 'master' into commonDb
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-20 16:41:50 +02:00
1758fc4297 Full CommonDB untested
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-20 16:41:07 +02:00
yoyosource
f152d790ce Add some api and linkage types
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Add LinkageProcessor
2022-09-19 09:13:07 +02:00
yoyosource
f8e18ec79f Add MethodBuilder
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Add ParameterBuilder
Add MinVersion
2022-09-19 09:01:05 +02:00
yoyosource
8c574c1975 Merge branch 'master' into Linkage
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-19 08:36:30 +02:00
1dea71993d Merge pull request 'Add BetterException handling for exceptions thrown in tabComplete and validate since there now exception should be thrown there' (#18) from CMDAPIBetterExceptionHandling into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #18
2022-09-13 15:50:50 +02:00
yoyosource
9439cc81a5 Add BetterException handling for exceptions in tabComplete and validate since there now exception should be thrown there
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-09 20:48:52 +02:00
c1e175c3e8 WIP current state
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-08 10:42:57 +02:00
9df92595b2 „testsrc/de/steamwar/command/SimpleCommand.java“ ändern
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-06 10:39:45 +02:00
yoyosource
c4a8ae4c9f Update some stuff
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2022-09-04 21:25:23 +02:00
yoyosource
3da9e9bc69 Add linkage prototype
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-03 17:15:21 +02:00
yoyosource
6e6d34905a Fix AbstractSWCommand register
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-09-01 17:34:49 +02:00
9ccb678522 WIP commonDB
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2022-09-01 17:17:39 +02:00
1113ae24e4 Merge branch 'master' into commonDb
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2022-08-31 09:19:04 +02:00
211f6258b5 Merge pull request 'Add AbstractSWCommand.AllowNull' (#16) from CMDAPIAnnotationAllowNull into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #16
2022-08-28 21:03:06 +02:00
yoyosource
59361e14c0 Add AbstractSWCommand.AllowNull
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-27 18:03:16 +02:00
yoyosource
3701f6f5ff Fix validators
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-22 21:06:10 +02:00
yoyosource
384605c7ce Merge branch 'CMDAPIRemoveGuards'
# Conflicts:
#	src/de/steamwar/command/CommandPart.java
#	src/de/steamwar/command/SWCommandUtils.java
2022-08-22 20:41:10 +02:00
yoyosource
2c6ebc82f3 Fix internal error
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-22 18:30:23 +02:00
yoyosource
3829cd6706 Add AbstractSWCommand.OptionalValue.onlyUINIG
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-22 18:07:35 +02:00
6cb3bc5ff5 Merge pull request 'Update AbstractValidator' (#14) from CMDAPIBetterValidationAPI into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #14
2022-08-17 20:38:43 +02:00
yoyosource
21cb3b266a Add AbstractValidator.Validator.set
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-17 18:35:59 +02:00
yoyosource
a4ac3fa8f0 Remove unused imports
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-17 18:25:08 +02:00
yoyosource
10893388c7 Update AbstractValidator
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-17 18:11:34 +02:00
yoyosource
3fa992b5c9 Remove guard system
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
2022-08-16 22:20:17 +02:00
d71896979e Merge pull request 'Update list usage to collection in SWTypeMapperCreator' (#13) from CMDAPIUpdateListUsage into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #13
2022-08-16 18:19:16 +02:00
yoyosource
f338742438 Update list usage to collection in SWTypeMapperCreator
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-16 18:14:59 +02:00
b0fe8b2979 Merge pull request 'Add better int and long handling for StaticValue annotation' (#12) from CMDAPIOptimizeISEForNumber into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #12
2022-08-16 18:14:18 +02:00
yoyosource
498a638790 Add one last test
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-16 18:11:10 +02:00
yoyosource
c2795f0f34 Add StaticValueCommand and StaticValueCommandTest
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-16 18:04:55 +02:00
yoyosource
696c03b851 Add better int and long handling for StaticValue annotation
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-16 17:53:59 +02:00
23a72f2acf Merge pull request 'Add better boolean handling for StaticValue annotation' (#11) from CMDAPIOptimizeISEForBoolean into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #11
2022-08-16 17:53:06 +02:00
yoyosource
b28178b915 Add better boolean handling for StaticValue annotation
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-16 17:12:27 +02:00
yoyosource
5050580774 Add better boolean handling for StaticValue annotation
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-08-16 17:11:16 +02:00
47bd9af03e Merge pull request 'Add api to enable caching for TypeMapper' (#10) from CMDAPICaching into master
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Reviewed-on: #10
2022-07-28 18:07:54 +02:00
yoyosource
059f28fbb3 Add api to enable caching for TypeMapper
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
2022-07-28 08:46:31 +02:00
c5bed0acfa WIP commonDb
Einige Prüfungen sind fehlgeschlagen
SteamWarCI Build failed
Signed-off-by: Lixfel <agga-games@gmx.de>
2022-07-01 21:37:33 +02:00
4f719e30ac First basic untested implementation
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2022-06-23 14:58:33 +02:00
3d280ac051 Merge branch 'master' into commonDb
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
# Conflicts:
#	src/de/steamwar/command/SWCommandUtils.java
2022-06-23 10:33:35 +02:00
29bc98889a Prototyping new db interface
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2022-06-11 14:20:14 +02:00
d6c29a25b9 More commonDB
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2022-06-08 08:00:31 +02:00
yoyosource
b25bf2a79c Fix SWCommandUtils 2022-06-07 14:25:45 +02:00
yoyosource
57ab89e058 Fix SWCommandUtils 2022-06-07 14:25:45 +02:00
yoyosource
800e507330 Fix SWCommandUtils
Add SWTypeMapperCreator
2022-06-07 14:25:45 +02:00
yoyosource
f619797824 Fix enum mapper 2022-06-07 14:25:45 +02:00
6d29507973 WIP commonDB
Alle Prüfungen waren erfolgreich
SteamWarCI Build successful
Signed-off-by: Lixfel <agga-games@gmx.de>
2022-05-24 09:16:44 +02:00
105 geänderte Dateien mit 7697 neuen und 587 gelöschten Zeilen

22
build.gradle Normale Datei → Ausführbare Datei
Datei anzeigen

@ -74,15 +74,29 @@ repositories {
}
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.22'
testCompileOnly 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.22'
compileOnly 'org.projectlombok:lombok:1.18.32'
testCompileOnly 'org.projectlombok:lombok:1.18.32'
annotationProcessor 'org.projectlombok:lombok:1.18.32'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.32'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.hamcrest:hamcrest:2.2'
compileOnly 'org.xerial:sqlite-jdbc:3.46.0.0'
}
task buildResources {
doLast {
File to = new File("${buildDir}/classes/java/main/META-INF/services/javax.annotation.processing.Processor")
to.parentFile.mkdirs()
if (!to.exists()) {
to.createNewFile()
to.append("de.steamwar.linkage.LinkageProcessor\n")
}
}
}
classes.finalizedBy(buildResources)
task buildProject {
description 'Build this project'
group "Steamwar"

Datei anzeigen

@ -0,0 +1,34 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar;
import java.lang.reflect.InvocationTargetException;
public class ImplementationProvider {
private ImplementationProvider() {}
public static <T> T getImpl(String className) {
try {
return (T) Class.forName(className).getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) {
throw new SecurityException("Could not load implementation", e);
}
}
}

Datei anzeigen

@ -20,25 +20,30 @@
package de.steamwar.command;
import java.lang.annotation.*;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.IntPredicate;
import java.util.function.BiPredicate;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public abstract class AbstractSWCommand<T> {
private static final Map<Class<AbstractSWCommand<?>>, List<AbstractSWCommand<?>>> dependencyMap = new HashMap<>();
private Class<?> clazz; // This is used in createMappings()
private boolean initialized = false;
protected final List<SubCommand<T>> commandList = new ArrayList<>();
protected final List<SubCommand<T>> commandHelpList = new ArrayList<>();
protected final List<SubCommand<T>> helpCommandList = new ArrayList<>();
private final Map<String, AbstractTypeMapper<T, ?>> localTypeMapper = new HashMap<>();
private final Map<String, AbstractGuardChecker<T>> localGuardChecker = new HashMap<>();
private final Map<String, AbstractValidator<T, ?>> localValidators = new HashMap<>();
protected AbstractSWCommand(Class<T> clazz, String command) {
@ -47,6 +52,13 @@ public abstract class AbstractSWCommand<T> {
protected AbstractSWCommand(Class<T> clazz, String command, String... aliases) {
this.clazz = clazz;
PartOf partOf = this.getClass().getAnnotation(PartOf.class);
if (partOf != null) {
dependencyMap.computeIfAbsent((Class<AbstractSWCommand<?>>) partOf.value(), k -> new ArrayList<>()).add(this);
return;
}
createAndSafeCommand(command, aliases);
unregister();
register();
@ -66,21 +78,22 @@ public abstract class AbstractSWCommand<T> {
System.out.println(message.get());
}
protected void sendMessage(T sender, String message, Object[] args) {}
protected void sendMessage(T sender, String message, Object[] args) {
}
protected void initialisePartOf(AbstractSWCommand parent) {
}
protected final void execute(T sender, String alias, String[] args) {
initialize();
List<Runnable> errors = new ArrayList<>();
try {
if (!commandList.stream().anyMatch(s -> s.invoke(errors::add, sender, alias, args))) {
if (!errors.isEmpty()) {
if (commandList.stream().noneMatch(s -> s.invoke(errors::add, sender, alias, args))) {
errors.forEach(Runnable::run);
} else return;
if (errors.isEmpty() && helpCommandList.stream().noneMatch(s -> s.invoke(errors::add, sender, alias, args))) {
errors.forEach(Runnable::run);
return;
}
commandHelpList.stream().anyMatch(s -> s.invoke((ignore) -> {}, sender, alias, args));
}
} catch (CommandNoHelpException e) {
// Ignored
} catch (CommandFrameworkException e) {
commandSystemError(sender, e);
throw e;
@ -90,95 +103,45 @@ public abstract class AbstractSWCommand<T> {
protected final List<String> tabComplete(T sender, String alias, String[] args) throws IllegalArgumentException {
initialize();
String string = args[args.length - 1].toLowerCase();
return commandList.stream()
return Stream.concat(commandList.stream(), helpCommandList.stream())
.filter(s -> !s.noTabComplete)
.map(s -> s.tabComplete(sender, args))
.filter(Objects::nonNull)
.flatMap(Collection::stream)
.filter(s -> !s.isEmpty())
.filter(s -> s.toLowerCase().startsWith(string))
.filter(s -> s.toLowerCase().startsWith(string) || string.startsWith(s.toLowerCase()))
.distinct()
.collect(Collectors.toList());
}
private void initialize() {
private synchronized void initialize() {
if (initialized) return;
createMapping();
}
private synchronized void createMapping() {
List<Method> methods = methods();
List<Method> methods = methods().stream()
.filter(this::validateMethod)
.collect(Collectors.toList());
for (Method method : methods) {
Cached cached = method.getAnnotation(Cached.class);
addMapper(Mapper.class, method, i -> i == 0, false, AbstractTypeMapper.class, (anno, typeMapper) -> {
this.<Mapper, AbstractTypeMapper<?, ?>>add(Mapper.class, method, (anno, typeMapper) -> {
TabCompletionCache.add(typeMapper, cached);
if (anno.local()) {
localTypeMapper.putIfAbsent(anno.value(), (AbstractTypeMapper<T, ?>) typeMapper);
} else {
SWCommandUtils.getMAPPER_FUNCTIONS().putIfAbsent(anno.value(), typeMapper);
}
(anno.local() ? ((Map) localTypeMapper) : SWCommandUtils.getMAPPER_FUNCTIONS()).put(anno.value(), typeMapper);
});
addMapper(ClassMapper.class, method, i -> i == 0, false, AbstractTypeMapper.class, (anno, typeMapper) -> {
this.<ClassMapper, AbstractTypeMapper<?, ?>>add(ClassMapper.class, method, (anno, typeMapper) -> {
TabCompletionCache.add(typeMapper, cached);
if (anno.local()) {
localTypeMapper.putIfAbsent(anno.value().getTypeName(), (AbstractTypeMapper<T, ?>) typeMapper);
} else {
SWCommandUtils.getMAPPER_FUNCTIONS().putIfAbsent(anno.value().getTypeName(), typeMapper);
}
(anno.local() ? ((Map) localTypeMapper) : SWCommandUtils.getMAPPER_FUNCTIONS()).put(anno.value().getName(), typeMapper);
});
addGuard(Guard.class, method, i -> i == 0, false, AbstractGuardChecker.class, (anno, guardChecker) -> {
if (anno.local()) {
localGuardChecker.putIfAbsent(anno.value(), (AbstractGuardChecker<T>) guardChecker);
} else {
SWCommandUtils.getGUARD_FUNCTIONS().putIfAbsent(anno.value(), guardChecker);
}
this.<Validator, AbstractValidator<?, ?>>add(Validator.class, method, (anno, validator) -> {
(anno.local() ? ((Map) localValidators) : SWCommandUtils.getVALIDATOR_FUNCTIONS()).put(anno.value(), validator);
});
addGuard(ClassGuard.class, method, i -> i == 0, false, AbstractGuardChecker.class, (anno, guardChecker) -> {
if (anno.local()) {
localGuardChecker.putIfAbsent(anno.value().getTypeName(), (AbstractGuardChecker<T>) guardChecker);
} else {
SWCommandUtils.getGUARD_FUNCTIONS().putIfAbsent(anno.value().getTypeName(), guardChecker);
}
});
addValidator(Validator.class, method, i -> i == 0, false, AbstractValidator.class, (anno, validator) -> {
if (anno.local()) {
localValidators.putIfAbsent(anno.value(), (AbstractValidator<T, ?>) validator);
} else {
SWCommandUtils.getVALIDATOR_FUNCTIONS().putIfAbsent(anno.value(), validator);
}
});
addValidator(ClassValidator.class, method, i -> i == 0, false, AbstractValidator.class, (anno, validator) -> {
if (anno.local()) {
localValidators.putIfAbsent(anno.value().getTypeName(), (AbstractValidator<T, ?>) validator);
} else {
SWCommandUtils.getVALIDATOR_FUNCTIONS().putIfAbsent(anno.value().getTypeName(), validator);
}
this.<ClassValidator, AbstractValidator<?, ?>>add(ClassValidator.class, method, (anno, validator) -> {
(anno.local() ? ((Map) localValidators) : SWCommandUtils.getVALIDATOR_FUNCTIONS()).put(anno.value().getName(), validator);
});
}
for (Method method : methods) {
add(Register.class, method, i -> i > 0, true, null, (anno, parameters) -> {
if (!anno.help()) return;
if (parameters.length != 2) {
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking parameters or has too many");
}
if (!parameters[parameters.length - 1].isVarArgs()) {
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the varArgs parameters as last Argument");
}
if (parameters[parameters.length - 1].getType().getComponentType() != String.class) {
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the varArgs parameters of type '" + String.class.getTypeName() + "' as last Argument");
return;
}
commandHelpList.add(new SubCommand<>(this, method, anno.value(), new HashMap<>(), new HashMap<>(), localGuardChecker, true, null, anno.noTabComplete()));
});
add(Register.class, method, i -> i > 0, true, null, (anno, parameters) -> {
if (anno.help()) return;
add(Register.class, method, true, (anno, parameters) -> {
for (int i = 1; i < parameters.length; i++) {
Parameter parameter = parameters[i];
Class<?> clazz = parameter.getType();
if (parameter.isVarArgs() && i == parameters.length - 1) {
clazz = parameter.getType().getComponentType();
}
if (parameter.isVarArgs()) clazz = clazz.getComponentType();
Mapper mapper = parameter.getAnnotation(Mapper.class);
if (clazz.isEnum() && mapper == null && !SWCommandUtils.getMAPPER_FUNCTIONS().containsKey(clazz.getTypeName())) {
continue;
@ -189,78 +152,103 @@ public abstract class AbstractSWCommand<T> {
return;
}
}
commandList.add(new SubCommand<>(this, method, anno.value(), localTypeMapper, localValidators, localGuardChecker, false, anno.description(), anno.noTabComplete()));
commandList.add(new SubCommand<>(this, method, anno.value(), localTypeMapper, localValidators, anno.description(), anno.noTabComplete()));
});
}
this.commandList.sort((o1, o2) -> {
int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length);
if (compare != 0) {
return compare;
} else {
return Integer.compare(o1.comparableValue, o2.comparableValue);
}
if (dependencyMap.containsKey(this.getClass())) {
dependencyMap.get(this.getClass()).forEach(abstractSWCommand -> {
abstractSWCommand.localTypeMapper.putAll((Map) localTypeMapper);
abstractSWCommand.localValidators.putAll((Map) localValidators);
abstractSWCommand.initialisePartOf(this);
abstractSWCommand.initialize();
commandList.addAll((Collection) abstractSWCommand.commandList);
});
commandHelpList.sort((o1, o2) -> {
int compare = Integer.compare(-o1.subCommand.length, -o2.subCommand.length);
if (compare != 0) {
return compare;
}
Collections.sort(commandList);
commandList.removeIf(subCommand -> {
if (subCommand.isHelp) {
helpCommandList.add(subCommand);
return true;
} else {
return Integer.compare(o1.method.getDeclaringClass() == AbstractSWCommand.class ? 1 : 0,
o2.method.getDeclaringClass() == AbstractSWCommand.class ? 1 : 0);
return false;
}
});
initialized = true;
}
private <T extends Annotation> void add(Class<T> annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class<?> returnType, BiConsumer<T, Parameter[]> consumer) {
private boolean validateMethod(Method method) {
if (!checkType(method.getAnnotations(), method.getReturnType(), false, annotation -> {
CommandMetaData.Method methodMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Method.class);
if (methodMetaData == null) return (aClass, varArg) -> true;
if (method.getParameterCount() > methodMetaData.maxParameterCount() || method.getParameterCount() < methodMetaData.minParameterCount())
return (aClass, varArg) -> false;
return (aClass, varArg) -> {
Class<?>[] types = methodMetaData.value();
if (types == null) return true;
for (Class<?> type : types) {
if (type.isAssignableFrom(aClass)) return true;
}
return false;
};
}, "The method '" + method + "'")) return false;
boolean valid = true;
for (Parameter parameter : method.getParameters()) {
if (!checkType(parameter.getAnnotations(), parameter.getType(), parameter.isVarArgs(), annotation -> {
CommandMetaData.Parameter parameterMetaData = annotation.annotationType().getAnnotation(CommandMetaData.Parameter.class);
if (parameterMetaData == null) return (aClass, varArg) -> true;
Class<?> handler = parameterMetaData.handler();
if (BiPredicate.class.isAssignableFrom(handler)) {
try {
return (BiPredicate<Class<?>, Boolean>) handler.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException |
NoSuchMethodException e) {
}
}
return (aClass, varArg) -> {
if (varArg) aClass = aClass.getComponentType();
Class<?>[] types = parameterMetaData.value();
if (types == null) return true;
for (Class<?> current : types) {
if (current.isAssignableFrom(aClass)) return true;
}
return false;
};
}, "The parameter '" + parameter + "'")) valid = false;
}
return valid;
}
private boolean checkType(Annotation[] annotations, Class<?> clazz, boolean varArg, Function<Annotation, BiPredicate<Class<?>, Boolean>> toApplicableTypes, String warning) {
boolean valid = true;
for (Annotation annotation : annotations) {
BiPredicate<Class<?>, Boolean> predicate = toApplicableTypes.apply(annotation);
if (!predicate.test(clazz, varArg)) {
commandSystemWarning(() -> warning + " is using an unsupported annotation of type '" + annotation.annotationType().getName() + "'");
valid = false;
}
}
return valid;
}
private <T extends Annotation> void add(Class<T> annotation, Method method, boolean firstParameter, BiConsumer<T, Parameter[]> consumer) {
T[] anno = SWCommandUtils.getAnnotation(method, annotation);
if (anno == null || anno.length == 0) return;
Parameter[] parameters = method.getParameters();
if (!parameterTester.test(parameters.length)) {
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking parameters or has too many");
return;
}
if (firstParameter && !clazz.isAssignableFrom(parameters[0].getType())) {
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the first parameter of type '" + clazz.getTypeName() + "'");
return;
}
if (returnType != null && !returnType.isAssignableFrom(method.getReturnType())) {
commandSystemWarning(() -> "The method '" + method.toString() + "' is lacking the desired return type '" + returnType.getTypeName() + "'");
return;
}
Arrays.stream(anno).forEach(t -> consumer.accept(t, parameters));
}
private <T extends Annotation> void addMapper(Class<T> annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class<?> returnType, BiConsumer<T, AbstractTypeMapper<?, ?>> consumer) {
add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> {
private <T extends Annotation, K> void add(Class<T> annotation, Method method, BiConsumer<T, K> consumer) {
add(annotation, method, false, (anno, parameters) -> {
try {
method.setAccessible(true);
consumer.accept(anno, (AbstractTypeMapper<T, ?>) method.invoke(this));
} catch (Exception e) {
throw new SecurityException(e.getMessage(), e);
}
});
}
private <T extends Annotation> void addValidator(Class<T> annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class<?> returnType, BiConsumer<T, AbstractValidator<T, ?>> consumer) {
add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> {
try {
method.setAccessible(true);
consumer.accept(anno, (AbstractValidator<T, ?>) method.invoke(this));
} catch (Exception e) {
throw new SecurityException(e.getMessage(), e);
}
});
}
@Deprecated
private <T extends Annotation> void addGuard(Class<T> annotation, Method method, IntPredicate parameterTester, boolean firstParameter, Class<?> returnType, BiConsumer<T, AbstractGuardChecker<?>> consumer) {
add(annotation, method, parameterTester, firstParameter, returnType, (anno, parameters) -> {
try {
method.setAccessible(true);
consumer.accept(anno, (AbstractGuardChecker<T>) method.invoke(this));
consumer.accept(anno, (K) method.invoke(this));
} catch (Exception e) {
throw new SecurityException(e.getMessage(), e);
}
@ -284,12 +272,29 @@ public abstract class AbstractSWCommand<T> {
return methods;
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface PartOf {
Class<?> value();
}
// --- Annotation for the command ---
/**
* Annotation for registering a method as a command
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Repeatable(Register.Registeres.class)
@CommandMetaData.Method(value = void.class, minParameterCount = 1)
protected @interface Register {
/**
* Identifier of subcommand
*/
String[] value() default {};
@Deprecated
boolean help() default false;
String[] description() default {};
@ -298,6 +303,7 @@ public abstract class AbstractSWCommand<T> {
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@CommandMetaData.Method(value = void.class, minParameterCount = 1)
@interface Registeres {
Register[] value();
}
@ -305,14 +311,41 @@ public abstract class AbstractSWCommand<T> {
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD})
@CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0)
@CommandMetaData.ImplicitTypeMapper(handler = Mapper.Handler.class)
protected @interface Mapper {
String value();
boolean local() default false;
class Handler<T> implements AbstractTypeMapper<T, Object> {
private AbstractTypeMapper<T, Object> inner;
public Handler(AbstractSWCommand.Mapper mapper, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
inner = (AbstractTypeMapper<T, Object>) SWCommandUtils.getTypeMapper(mapper.value(), localTypeMapper);
}
@Override
public Object map(T sender, PreviousArguments previousArguments, String s) {
return inner.map(sender, previousArguments, s);
}
@Override
public boolean validate(T sender, Object value, MessageSender messageSender) {
return inner.validate(sender, value, messageSender);
}
@Override
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
return inner.tabCompletes(sender, previousArguments, s);
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0)
protected @interface ClassMapper {
Class<?> value();
@ -321,48 +354,58 @@ public abstract class AbstractSWCommand<T> {
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@CommandMetaData.Method(value = AbstractTypeMapper.class, maxParameterCount = 0)
protected @interface Cached {
long cacheDuration() default 5;
TimeUnit timeUnit() default TimeUnit.SECONDS;
boolean global() default false;
}
@Deprecated
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD})
protected @interface Guard {
String value() default "";
boolean local() default false;
}
@Deprecated
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
protected @interface ClassGuard {
Class<?> value();
boolean local() default false;
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.METHOD})
@CommandMetaData.Method(value = AbstractValidator.class, maxParameterCount = 0)
@CommandMetaData.ImplicitValidator(handler = Validator.Handler.class, order = 0)
protected @interface Validator {
String value() default "";
boolean local() default false;
boolean invert() default false;
class Handler<T> implements AbstractValidator<T, Object> {
private AbstractValidator<T, Object> inner;
private boolean invert;
public Handler(AbstractSWCommand.Validator validator, Class<?> clazz, Map<String, AbstractValidator<T, ?>> localValidator) {
inner = (AbstractValidator<T, Object>) SWCommandUtils.getValidator(validator, clazz, localValidator);
invert = validator.invert();
}
@Override
public boolean validate(T sender, Object value, MessageSender messageSender) {
return inner.validate(sender, value, messageSender) ^ invert;
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@CommandMetaData.Method(value = AbstractValidator.class, maxParameterCount = 0)
protected @interface ClassValidator {
Class<?> value();
boolean local() default false;
}
// --- Implicit TypeMapper ---
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.Parameter({String.class, int.class, Integer.class, long.class, Long.class, boolean.class, Boolean.class})
@CommandMetaData.ImplicitTypeMapper(handler = StaticValue.Handler.class)
protected @interface StaticValue {
String[] value();
@ -371,10 +414,54 @@ public abstract class AbstractSWCommand<T> {
* and can be set to true if you want to allow int as well as boolean as annotated parameter types.
* The value array needs to be at least 2 long for this flag to be considered.
* While using an int, the value will represent the index into the value array.
* While using a boolean, the value array must only be 2 long and the value will be {@code false}
* for the first index and {@code true} for the second index.
* While using a boolean, the {@link #falseValues()} defines which indices are
* considered {@code false} or {@code true}.
*/
boolean allowISE() default false;
int[] falseValues() default {0};
class Handler<T> implements AbstractTypeMapper<T, Object> {
private AbstractTypeMapper inner;
public Handler(StaticValue staticValue, Class<?> clazz) {
if (clazz == String.class) {
inner = SWCommandUtils.createMapper(staticValue.value());
return;
}
if (!staticValue.allowISE()) {
throw new IllegalArgumentException("The parameter type '" + clazz.getTypeName() + "' is not supported by the StaticValue annotation");
}
if (clazz == boolean.class || clazz == Boolean.class) {
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
Set<Integer> falseValues = new HashSet<>();
for (int i : staticValue.falseValues()) falseValues.add(i);
inner = SWCommandUtils.createMapper(s -> {
int index = tabCompletes.indexOf(s);
return index == -1 ? null : !falseValues.contains(index);
}, (commandSender, s) -> tabCompletes);
} else if (clazz == int.class || clazz == Integer.class || clazz == long.class || clazz == Long.class) {
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
inner = SWCommandUtils.createMapper(s -> {
Number index = tabCompletes.indexOf(s);
return index.longValue() == -1 ? null : index;
}, (commandSender, s) -> tabCompletes);
} else {
throw new IllegalArgumentException("The parameter type '" + clazz.getTypeName() + "' is not supported by the StaticValue annotation");
}
}
@Override
public Object map(T sender, PreviousArguments previousArguments, String s) {
return inner.map(sender, previousArguments, s);
}
@Override
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
return inner.tabCompletes(sender, previousArguments, s);
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@ -384,10 +471,18 @@ public abstract class AbstractSWCommand<T> {
* Will pe parsed against the TypeMapper specified by the parameter or annotation.
*/
String value();
/**
* The method name stands for 'onlyUseIfNoneIsGiven'.
*/
boolean onlyUINIG() default false;
}
// --- Implicit Validator ---
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.ImplicitValidator(handler = ErrorMessage.Handler.class, order = Integer.MAX_VALUE)
protected @interface ErrorMessage {
/**
* Error message to be displayed when the parameter is invalid.
@ -398,5 +493,219 @@ public abstract class AbstractSWCommand<T> {
* This is the short form for 'allowEmptyArrays'.
*/
boolean allowEAs() default true;
class Handler<T> implements AbstractValidator<T, Object> {
private AbstractSWCommand.ErrorMessage errorMessage;
public Handler(AbstractSWCommand.ErrorMessage errorMessage) {
this.errorMessage = errorMessage;
}
@Override
public boolean validate(T sender, Object value, MessageSender messageSender) {
if (value == null) messageSender.send(errorMessage.value());
if (!errorMessage.allowEAs() && value != null && value.getClass().isArray() && Array.getLength(value) == 0) {
messageSender.send(errorMessage.value());
return false;
}
return value != null;
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
protected @interface AllowNull {
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class})
@CommandMetaData.ImplicitValidator(handler = Min.Handler.class, order = 2)
protected @interface Min {
int intValue() default Integer.MIN_VALUE;
long longValue() default Long.MIN_VALUE;
float floatValue() default Float.MIN_VALUE;
double doubleValue() default Double.MIN_VALUE;
boolean inclusive() default true;
class Handler<T> implements AbstractValidator<T, Number> {
private int value;
private Function<Number, Number> comparator;
public Handler(AbstractSWCommand.Min min, Class<?> clazz) {
this.value = min.inclusive() ? 0 : 1;
this.comparator = createComparator("Min", clazz, min.intValue(), min.longValue(), min.floatValue(), min.doubleValue());
}
@Override
public boolean validate(T sender, Number value, MessageSender messageSender) {
if (value == null) return true;
return (comparator.apply(value).intValue()) >= this.value;
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.Parameter({int.class, Integer.class, long.class, Long.class, float.class, Float.class, double.class, Double.class})
@CommandMetaData.ImplicitValidator(handler = Max.Handler.class, order = 2)
protected @interface Max {
int intValue() default Integer.MAX_VALUE;
long longValue() default Long.MAX_VALUE;
float floatValue() default Float.MAX_VALUE;
double doubleValue() default Double.MAX_VALUE;
boolean inclusive() default true;
class Handler<T> implements AbstractValidator<T, Number> {
private int value;
private Function<Number, Number> comparator;
public Handler(AbstractSWCommand.Max max, Class<?> clazz) {
this.value = max.inclusive() ? 0 : -1;
this.comparator = createComparator("Max", clazz, max.intValue(), max.longValue(), max.floatValue(), max.doubleValue());
}
@Override
public boolean validate(T sender, Number value, MessageSender messageSender) {
if (value == null) return true;
return (comparator.apply(value).intValue()) <= this.value;
}
}
}
private static Function<Number, Number> createComparator(String type, Class<?> clazz, int iValue, long lValue, float fValue, double dValue) {
if (clazz == int.class || clazz == Integer.class) {
return number -> Integer.compare(number.intValue(), iValue);
} else if (clazz == long.class || clazz == Long.class) {
return number -> Long.compare(number.longValue(), lValue);
} else if (clazz == float.class || clazz == Float.class) {
return number -> Float.compare(number.floatValue(), fValue);
} else if (clazz == double.class || clazz == Double.class) {
return number -> Double.compare(number.doubleValue(), dValue);
} else {
throw new IllegalArgumentException(type + " annotation is not supported for " + clazz);
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.ImplicitTypeMapper(handler = Length.Handler.class)
protected @interface Length {
int min() default 0;
int max() default Integer.MAX_VALUE;
class Handler<T> implements AbstractTypeMapper<T, Object> {
private int min;
private int max;
private AbstractTypeMapper<T, Object> inner;
public Handler(Length length, AbstractTypeMapper<T, Object> inner) {
this.min = length.min();
this.max = length.max();
this.inner = inner;
}
@Override
public Object map(T sender, PreviousArguments previousArguments, String s) {
if (s.length() < min || s.length() > max) return null;
return inner.map(sender, previousArguments, s);
}
@Override
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
List<String> tabCompletes = inner.tabCompletes(sender, previousArguments, s)
.stream()
.filter(str -> str.length() >= min)
.map(str -> str.substring(0, Math.min(str.length(), max)))
.collect(Collectors.toList());
if (s.length() < min) {
tabCompletes.add(0, s);
}
return tabCompletes;
}
@Override
public String normalize(T sender, String s) {
return inner.normalize(sender, s);
}
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@CommandMetaData.Parameter(handler = ArrayLength.Type.class)
@CommandMetaData.ImplicitTypeMapper(handler = ArrayLength.HandlerTypeMapper.class)
@CommandMetaData.ImplicitValidator(handler = ArrayLength.HandlerValidator.class, order = 1)
protected @interface ArrayLength {
int min() default 0;
int max() default Integer.MAX_VALUE;
class Type implements BiPredicate<Class<?>, Boolean> {
@Override
public boolean test(Class<?> clazz, Boolean isVarArgs) {
return clazz.isArray();
}
}
class HandlerTypeMapper<T> implements AbstractTypeMapper<T, Object> {
private int max;
private AbstractTypeMapper<T, Object> inner;
public HandlerTypeMapper(ArrayLength arrayLength, AbstractTypeMapper<T, Object> inner) {
this.max = arrayLength.max();
this.inner = inner;
}
@Override
public Object map(T sender, PreviousArguments previousArguments, String s) {
return inner.map(sender, previousArguments, s);
}
@Override
public Collection<String> tabCompletes(T sender, PreviousArguments previousArguments, String s) {
Object[] mapped = previousArguments.getMappedArg(0);
if (mapped.length >= max) return Collections.emptyList();
return inner.tabCompletes(sender, previousArguments, s);
}
@Override
public String normalize(T sender, String s) {
return inner.normalize(sender, s);
}
}
class HandlerValidator<T> implements AbstractValidator<T, Object> {
private int min;
private int max;
public HandlerValidator(ArrayLength arrayLength) {
this.min = arrayLength.min();
this.max = arrayLength.max();
}
@Override
public boolean validate(T sender, Object value, MessageSender messageSender) {
if (value == null) return true;
int length = Array.getLength(value);
return length >= min && length <= max;
}
}
}
}

Datei anzeigen

@ -25,12 +25,38 @@ public interface AbstractTypeMapper<K, T> extends AbstractValidator<K, T> {
/**
* The CommandSender can be null!
*/
T map(K sender, String[] previousArguments, String s);
@Deprecated
default T map(K sender, String[] previousArguments, String s) {
throw new IllegalArgumentException("This method is deprecated and should not be used anymore!");
}
/**
* The CommandSender can be null!
*/
default T map(K sender, PreviousArguments previousArguments, String s) {
return map(sender, previousArguments.userArgs, s);
}
@Override
default boolean validate(K sender, T value, MessageSender messageSender) {
return true;
}
Collection<String> tabCompletes(K sender, String[] previousArguments, String s);
@Deprecated
default Collection<String> tabCompletes(K sender, String[] previousArguments, String s) {
throw new IllegalArgumentException("This method is deprecated and should not be used anymore!");
}
default Collection<String> tabCompletes(K sender, PreviousArguments previousArguments, String s) {
return tabCompletes(sender, previousArguments.userArgs, s);
}
/**
* Normalize the cache key by sender and user provided argument. <br>
* Note: The CommandSender can be null! <br>
* Returning null and the empty string are equivalent.
*/
default String normalize(K sender, String s) {
return null;
}
}

Datei anzeigen

@ -19,6 +19,12 @@
package de.steamwar.command;
import lombok.RequiredArgsConstructor;
import java.util.function.BooleanSupplier;
import java.util.function.Function;
import java.util.function.Predicate;
@FunctionalInterface
public interface AbstractValidator<K, T> {
@ -32,8 +38,59 @@ public interface AbstractValidator<K, T> {
*/
boolean validate(K sender, T value, MessageSender messageSender);
@Deprecated
default <C> Validator<C> validate(C value, MessageSender messageSender) {
return new Validator<>(value, messageSender);
}
@Deprecated
@RequiredArgsConstructor
class Validator<C> {
private final C value;
private final MessageSender messageSender;
private boolean valid = true;
public <M> Validator<M> map(Function<C, M> mapper) {
return new Validator<>(mapper.apply(value), messageSender).set(valid);
}
public Validator<C> set(boolean value) {
this.valid = value;
return this;
}
public Validator<C> and(Predicate<C> predicate) {
valid &= predicate.test(value);
return this;
}
public Validator<C> or(Predicate<C> predicate) {
valid |= predicate.test(value);
return this;
}
public Validator<C> errorMessage(String s, Object... args) {
if (!valid) messageSender.send(s, args);
return this;
}
public boolean result() {
return valid;
}
}
@FunctionalInterface
interface MessageSender {
void send(String s, Object... args);
default boolean send(boolean condition, String s, Object... args) {
if (condition) send(s, args);
return condition;
}
default boolean send(BooleanSupplier condition, String s, Object... args) {
return send(condition.getAsBoolean(), s, args);
}
}
}

Datei anzeigen

@ -21,39 +21,73 @@ package de.steamwar.command;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.lang.reflect.Executable;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.function.Function;
import java.util.stream.Stream;
public class CommandFrameworkException extends RuntimeException {
private InvocationTargetException invocationTargetException;
private String alias;
private String[] args;
private Function causeMessage;
private Throwable cause;
private Function stackTraceExtractor;
private String extraStackTraces;
private String message;
static CommandFrameworkException commandPartExceptions(String type, Throwable cause, String current, Class<?> clazzType, Executable executable, int index) {
return new CommandFrameworkException(e -> {
return CommandFrameworkException.class.getTypeName() + ": Error while " + type + " (" + current + ") to type " + clazzType.getTypeName() + " with parameter index " + index;
}, cause, exception -> {
StackTraceElement[] stackTraceElements = exception.getStackTrace();
int last = 0;
for (int i = 0; i < stackTraceElements.length; i++) {
if (stackTraceElements[i].getClassName().equals(CommandPart.class.getTypeName())) {
last = i;
break;
}
}
return Arrays.stream(stackTraceElements).limit(last - 1);
}, executable.getDeclaringClass().getTypeName() + "." + executable.getName() + "(Unknown Source)");
}
CommandFrameworkException(InvocationTargetException invocationTargetException, String alias, String[] args) {
super(invocationTargetException);
this.invocationTargetException = invocationTargetException;
this.alias = alias;
this.args = args;
this(e -> {
StringBuilder st = new StringBuilder();
st.append(e.getCause().getClass().getTypeName());
if (e.getCause().getMessage() != null) {
st.append(": ").append(e.getCause().getMessage());
}
if (alias != null && !alias.isEmpty()) {
st.append("\n").append("Performed command: " + alias + " " + String.join(" ", args));
}
return st.toString();
}, invocationTargetException, e -> {
StackTraceElement[] stackTraceElements = e.getCause().getStackTrace();
return Arrays.stream(stackTraceElements).limit(stackTraceElements.length - e.getStackTrace().length);
}, null);
}
private <T extends Throwable> CommandFrameworkException(Function<T, String> causeMessage, T cause, Function<T, Stream<StackTraceElement>> stackTraceExtractor, String extraStackTraces) {
super(causeMessage.apply(cause), cause);
this.causeMessage = causeMessage;
this.cause = cause;
this.stackTraceExtractor = stackTraceExtractor;
this.extraStackTraces = extraStackTraces;
}
public synchronized String getBuildStackTrace() {
if (message != null) {
return message;
}
StackTraceElement[] stackTraceElements = invocationTargetException.getCause().getStackTrace();
StringBuilder st = new StringBuilder();
st.append(invocationTargetException.getCause().getClass().getTypeName());
if (invocationTargetException.getCause().getMessage() != null) {
st.append(": ").append(invocationTargetException.getCause().getMessage());
}
st.append("\n");
if (alias != null && !alias.isEmpty()) {
st.append("Performed command: ").append(alias).append(" ").append(String.join(" ", args)).append("\n");
}
for (int i = 0; i < stackTraceElements.length - invocationTargetException.getStackTrace().length; i++) {
st.append("\tat ").append(stackTraceElements[i].toString()).append("\n");
st.append(causeMessage.apply(cause)).append("\n");
((Stream<StackTraceElement>) stackTraceExtractor.apply(cause)).forEach(stackTraceElement -> {
st.append("\tat ").append(stackTraceElement.toString()).append("\n");
});
if (extraStackTraces != null) {
st.append("\tat ").append(extraStackTraces).append("\n");
}
message = st.toString();
return message;

Datei anzeigen

@ -0,0 +1,92 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import java.lang.annotation.*;
public @interface CommandMetaData {
/**
* This annotation is only for internal use.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface Method {
Class<?>[] value();
int minParameterCount() default 0;
int maxParameterCount() default Integer.MAX_VALUE;
}
/**
* This annotation denotes what types are allowed as parameter types the annotation annotated with can use.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface Parameter {
Class<?>[] value() default {};
Class<?> handler() default void.class;
}
/**
* This annotation can be used in conjunction with a class that implements {@link AbstractTypeMapper} to
* create a custom type mapper for a parameter. The class must have one of two constructors with the following
* types:
* <ul>
* <li>Annotation this annotation annotates</li>
* <li>{@link Class}</li>
* <li>{@link AbstractTypeMapper}, optional, if not present only one per parameter</li>
* <li>{@link java.util.Map} with types {@link String} and {@link AbstractValidator}</li>
* </ul>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface ImplicitTypeMapper {
/**
* The validator class that should be used.
*/
Class<?> handler();
}
/**
* This annotation can be used in conjunction with a class that implements {@link AbstractValidator} to
* create a custom validator short hands for commands. The validator class must have one constructor with
* one of the following types:
* <ul>
* <li>Annotation this annotation annotates</li>
* <li>{@link Class}</li>
* <li>{@link java.util.Map} with types {@link String} and {@link AbstractValidator}</li>
* </ul>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
@interface ImplicitValidator {
/**
* The validator class that should be used.
*/
Class<?> handler();
/**
* Defines when this validator should be processed. Negative numbers denote that this will be
* processed before {@link AbstractSWCommand.Validator} and positive numbers
* denote that this will be processed after {@link AbstractSWCommand.Validator}.
*/
int order();
}
}

Datei anzeigen

@ -23,6 +23,7 @@ import lombok.AllArgsConstructor;
import lombok.Setter;
import java.lang.reflect.Array;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@ -31,7 +32,8 @@ import java.util.function.Consumer;
class CommandPart<T> {
private static final String[] EMPTY_ARRAY = new String[0];
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
@AllArgsConstructor
private static class CheckArgumentResult {
@ -41,50 +43,57 @@ class CommandPart<T> {
private AbstractSWCommand<T> command;
private AbstractTypeMapper<T, ?> typeMapper;
private AbstractValidator<T, Object> validator;
private AbstractGuardChecker<T> guardChecker;
private List<AbstractValidator<T, Object>> validators = new ArrayList<>();
private Class<?> varArgType;
private String optional;
private GuardCheckType guardCheckType;
private CommandPart<T> next = null;
@Setter
private boolean ignoreAsArgument = false;
public CommandPart(AbstractSWCommand<T> command, AbstractTypeMapper<T, ?> typeMapper, AbstractValidator<T, Object> validator, AbstractGuardChecker<T> guardChecker, Class<?> varArgType, String optional, GuardCheckType guardCheckType) {
@Setter
private boolean onlyUseIfNoneIsGiven = false;
private Parameter parameter;
private int parameterIndex;
public CommandPart(AbstractSWCommand<T> command, AbstractTypeMapper<T, ?> typeMapper, Class<?> varArgType, String optional, Parameter parameter, int parameterIndex) {
this.command = command;
this.typeMapper = typeMapper;
this.validator = validator;
this.guardChecker = guardChecker;
this.varArgType = varArgType;
this.optional = optional;
this.guardCheckType = guardCheckType;
this.parameter = parameter;
this.parameterIndex = parameterIndex;
validatePart();
if (optional != null && varArgType != null) {
throw new IllegalArgumentException("A vararg part can't have an optional part! In method " + parameter.getDeclaringExecutable() + " with parameter " + parameterIndex);
}
}
void addValidator(AbstractValidator<T, Object> validator) {
if (validator == null) return;
validators.add(validator);
}
public void setNext(CommandPart<T> next) {
if (varArgType != null) {
throw new IllegalArgumentException("There can't be a next part if this is a vararg part!");
throw new IllegalArgumentException("There can't be a next part if this is a vararg part! In method " + parameter.getDeclaringExecutable() + " with parameter " + parameterIndex);
}
this.next = next;
}
private void validatePart() {
if (guardCheckType == GuardCheckType.TAB_COMPLETE) {
throw new IllegalArgumentException("Tab complete is not allowed as a guard check type!");
public boolean isHelp() {
if (next == null) {
if (varArgType == null) {
return false;
}
if (optional != null && varArgType != null) {
throw new IllegalArgumentException("A vararg part can't have an optional part!");
}
if (optional != null) {
try {
typeMapper.map(null, EMPTY_ARRAY, optional);
} catch (Exception e) {
throw new IllegalArgumentException("The optional part is not valid!");
if (varArgType != String.class) {
return false;
}
return typeMapper == SWCommandUtils.STRING_MAPPER;
} else {
return next.isHelp();
}
}
@ -92,28 +101,32 @@ class CommandPart<T> {
if (varArgType != null) {
Object array = Array.newInstance(varArgType, args.length - startIndex);
for (int i = startIndex; i < args.length; i++) {
CheckArgumentResult validArgument = checkArgument(null, null, sender, args, i);
if (!validArgument.success) {
throw new CommandParseException();
}
CheckArgumentResult validArgument = checkArgument(null, sender, args, current, i);
if (!validArgument.success) throw new CommandParseException();
Array.set(array, i - startIndex, validArgument.value);
}
if (validator != null && !validator.validate(sender, array, (s, objects) -> {
for (AbstractValidator<T, Object> validator : validators) {
if (!validator.validate(sender, array, (s, objects) -> {
errors.accept(() -> command.sendMessage(sender, s, objects));
})) {
throw new CommandParseException();
})) throw new CommandParseException();
}
current.add(array);
return;
}
CheckArgumentResult validArgument = checkArgument(errors, null, sender, args, startIndex);
CheckArgumentResult validArgument = checkArgument(errors, sender, args, current, startIndex);
if (!validArgument.success && optional == null) {
throw new CommandParseException();
}
if (!validArgument.success) {
if (!ignoreAsArgument) {
current.add(typeMapper.map(sender, EMPTY_ARRAY, optional));
if (!onlyUseIfNoneIsGiven) {
current.add(typeMapper.map(sender, new PreviousArguments(EMPTY_STRING_ARRAY, EMPTY_OBJECT_ARRAY), optional));
} else if (startIndex >= args.length) {
current.add(typeMapper.map(sender, new PreviousArguments(EMPTY_STRING_ARRAY, EMPTY_OBJECT_ARRAY), optional));
} else {
throw new CommandParseException();
}
}
if (next != null) {
next.generateArgumentArray(errors, current, sender, args, startIndex);
@ -125,51 +138,23 @@ class CommandPart<T> {
}
if (next != null) {
next.generateArgumentArray(errors, current, sender, args, startIndex + 1);
} else if (startIndex + 1 < args.length) {
throw new CommandParseException();
}
}
public boolean guardCheck(T sender, String[] args, int startIndex) {
if (varArgType != null) {
for (int i = startIndex; i < args.length; i++) {
GuardResult guardResult = checkGuard(guardCheckType, sender, args, i);
if (guardResult == GuardResult.DENIED) {
throw new CommandNoHelpException();
}
if (guardResult == GuardResult.DENIED_WITH_HELP) {
return false;
}
}
return true;
}
GuardResult guardResult = checkGuard(guardCheckType, sender, args, startIndex);
if (guardResult == GuardResult.DENIED) {
if (optional != null && next != null) {
return next.guardCheck(sender, args, startIndex);
}
throw new CommandNoHelpException();
}
if (guardResult == GuardResult.DENIED_WITH_HELP) {
if (optional != null && next != null) {
return next.guardCheck(sender, args, startIndex);
}
return false;
}
if (next != null) {
return next.guardCheck(sender, args, startIndex + 1);
}
return true;
}
public void generateTabComplete(List<String> current, T sender, String[] args, int startIndex) {
public void generateTabComplete(List<String> current, T sender, String[] args, List<Object> mappedArgs, int startIndex) {
if (varArgType != null) {
List<Object> currentArgs = new ArrayList<>(mappedArgs);
List<Object> varArgs = new ArrayList<>();
for (int i = startIndex; i < args.length - 1; i++) {
CheckArgumentResult validArgument = checkArgument((ignore) -> {}, GuardCheckType.TAB_COMPLETE, sender, args, i);
if (!validArgument.success) {
return;
CheckArgumentResult validArgument = checkArgument((ignore) -> {}, sender, args, mappedArgs, i);
if (!validArgument.success) return;
varArgs.add(validArgument.value);
}
}
Collection<String> strings = tabCompletes(sender, args, args.length - 1);
currentArgs.add(varArgs.toArray());
Collection<String> strings = tabCompletes(sender, args, currentArgs, args.length - 1);
if (strings != null) {
current.addAll(strings);
}
@ -177,68 +162,65 @@ class CommandPart<T> {
}
if (args.length - 1 > startIndex) {
CheckArgumentResult checkArgumentResult = checkArgument((ignore) -> {}, GuardCheckType.TAB_COMPLETE, sender, args, startIndex);
CheckArgumentResult checkArgumentResult = checkArgument((ignore) -> {}, sender, args, mappedArgs, startIndex);
if (checkArgumentResult.success && next != null) {
next.generateTabComplete(current, sender, args, startIndex + 1);
if (!ignoreAsArgument) {
mappedArgs.add(checkArgumentResult.value);
}
next.generateTabComplete(current, sender, args, mappedArgs, startIndex + 1);
return;
}
if (optional != null && next != null) {
next.generateTabComplete(current, sender, args, startIndex);
next.generateTabComplete(current, sender, args, mappedArgs, startIndex);
}
return;
}
Collection<String> strings = tabCompletes(sender, args, startIndex);
Collection<String> strings = tabCompletes(sender, args, mappedArgs, startIndex);
if (strings != null) {
current.addAll(strings);
}
if (optional != null && next != null) {
next.generateTabComplete(current, sender, args, startIndex);
next.generateTabComplete(current, sender, args, mappedArgs, startIndex);
}
}
private Collection<String> tabCompletes(T sender, String[] args, int startIndex) {
return TabCompletionCache.tabComplete(sender, typeMapper, command, () -> {
return typeMapper.tabCompletes(sender, Arrays.copyOf(args, startIndex), args[startIndex]);
private Collection<String> tabCompletes(T sender, String[] args, List<Object> mappedArgs, int startIndex) {
return TabCompletionCache.tabComplete(sender, args[startIndex], (AbstractTypeMapper<Object, ?>) typeMapper, () -> {
try {
return typeMapper.tabCompletes(sender, new PreviousArguments(Arrays.copyOf(args, startIndex), mappedArgs.toArray()), args[startIndex]);
} catch (Throwable e) {
throw CommandFrameworkException.commandPartExceptions("tabcompleting", e, args[startIndex], (varArgType != null ? varArgType : parameter.getType()), parameter.getDeclaringExecutable(), parameterIndex);
}
});
}
private CheckArgumentResult checkArgument(Consumer<Runnable> errors, GuardCheckType guardCheckType, T sender, String[] args, int index) {
private CheckArgumentResult checkArgument(Consumer<Runnable> errors, T sender, String[] args, List<Object> mappedArgs, int index) {
Object value;
try {
value = typeMapper.map(sender, new PreviousArguments(Arrays.copyOf(args, index), mappedArgs.toArray()), args[index]);
} catch (Exception e) {
return new CheckArgumentResult(false, null);
}
boolean success = true;
for (AbstractValidator<T, Object> validator : validators) {
try {
Object value = typeMapper.map(sender, Arrays.copyOf(args, index), args[index]);
if (validator != null && errors != null) {
if (!validator.validate(sender, value, (s, objects) -> {
errors.accept(() -> {
command.sendMessage(sender, s, objects);
});
})) {
return new CheckArgumentResult(false, null);
success = false;
value = null;
}
return new CheckArgumentResult(value != null, value);
} catch (Throwable e) {
throw CommandFrameworkException.commandPartExceptions("validating", e, args[index], (varArgType != null ? varArgType : parameter.getType()), parameter.getDeclaringExecutable(), parameterIndex);
}
if (value == null) {
return new CheckArgumentResult(false, null);
}
return new CheckArgumentResult(success, value);
}
GuardResult guardResult = checkGuard(guardCheckType, sender, args, index);
switch (guardResult) {
case ALLOWED:
return new CheckArgumentResult(true, value);
case DENIED:
throw new CommandNoHelpException();
case DENIED_WITH_HELP:
default:
return new CheckArgumentResult(false, null);
}
} catch (Exception e) {
return new CheckArgumentResult(false, null);
}
}
private GuardResult checkGuard(GuardCheckType guardCheckType, T sender, String[] args, int index) {
if (guardChecker != null && guardCheckType != null) {
return guardChecker.guard(sender, guardCheckType, Arrays.copyOf(args, index), args[index]);
}
return GuardResult.ALLOWED;
public Class<?> getType() {
return varArgType != null ? varArgType : parameter.getType();
}
}

Datei anzeigen

@ -0,0 +1,62 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class PreviousArguments {
public final String[] userArgs;
public final Object[] mappedArgs;
public PreviousArguments(String[] userArgs, Object[] mappedArgs) {
this.userArgs = userArgs;
this.mappedArgs = mappedArgs;
}
public String getUserArg(int index) {
return userArgs[userArgs.length - index - 1];
}
public <T> T getMappedArg(int index) {
return (T) mappedArgs[mappedArgs.length - index - 1];
}
public <T> Optional<T> getFirst(Class<T> clazz) {
for (Object o : mappedArgs) {
if (clazz.isInstance(o)) {
return Optional.of((T) o);
}
}
return Optional.empty();
}
public <T> List<T> getAll(Class<T> clazz) {
List<T> list = new ArrayList<>();
for (Object o : mappedArgs) {
if (clazz.isInstance(o)) {
list.add((T) o);
}
}
return list;
}
}

Datei anzeigen

@ -23,7 +23,6 @@ import lombok.Getter;
import lombok.experimental.UtilityClass;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
@ -37,36 +36,34 @@ public class SWCommandUtils {
@Getter
private final Map<String, AbstractTypeMapper<?, ?>> MAPPER_FUNCTIONS = new HashMap<>();
@Getter
@Deprecated
private final Map<String, AbstractGuardChecker<?>> GUARD_FUNCTIONS = new HashMap<>();
@Getter
private final Map<String, AbstractValidator<?, ?>> VALIDATOR_FUNCTIONS = new HashMap<>();
private SWTypeMapperCreator swTypeMapperCreator = (mapper, tabCompleter) -> new AbstractTypeMapper<Object, Object>() {
@Override
public Object map(Object sender, String[] previousArguments, String s) {
public Object map(Object sender, PreviousArguments previousArguments, String s) {
return mapper.apply(s);
}
@Override
public Collection<String> tabCompletes(Object sender, String[] previousArguments, String s) {
public Collection<String> tabCompletes(Object sender, PreviousArguments previousArguments, String s) {
return ((BiFunction<Object, Object, Collection<String>>) tabCompleter).apply(sender, s);
}
};
static final AbstractTypeMapper<Object, String> STRING_MAPPER = createMapper(s -> s, Collections::singletonList);
static {
addMapper(boolean.class, Boolean.class, createMapper(s -> {
if (s.equalsIgnoreCase("true")) return true;
if (s.equalsIgnoreCase("false")) return false;
return null;
}, s -> Arrays.asList("true", "false")));
addMapper(float.class, Float.class, createMapper(numberMapper(Float::parseFloat), numberCompleter(Float::parseFloat)));
addMapper(double.class, Double.class, createMapper(numberMapper(Double::parseDouble), numberCompleter(Double::parseDouble)));
addMapper(int.class, Integer.class, createMapper(numberMapper(Integer::parseInt), numberCompleter(Integer::parseInt)));
addMapper(long.class, Long.class, createMapper(numberMapper(Long::parseLong), numberCompleter(Long::parseLong)));
MAPPER_FUNCTIONS.put(String.class.getTypeName(), createMapper(s -> s, Collections::singletonList));
addMapper(float.class, Float.class, createMapper(numberMapper(Float::parseFloat), numberCompleter(Float::parseFloat, true)));
addMapper(double.class, Double.class, createMapper(numberMapper(Double::parseDouble), numberCompleter(Double::parseDouble, true)));
addMapper(int.class, Integer.class, createMapper(numberMapper(Integer::parseInt), numberCompleter(Integer::parseInt, false)));
addMapper(long.class, Long.class, createMapper(numberMapper(Long::parseLong), numberCompleter(Long::parseLong, false)));
MAPPER_FUNCTIONS.put(String.class.getTypeName(), STRING_MAPPER);
}
public static <T extends AbstractTypeMapper<K, V>, K, V> void init(SWTypeMapperCreator<T, K, V> swTypeMapperCreator) {
@ -78,88 +75,7 @@ public class SWCommandUtils {
MAPPER_FUNCTIONS.put(alternativeClazz.getTypeName(), mapper);
}
static <T> CommandPart<T> generateCommandPart(AbstractSWCommand<T> command, boolean help, String[] subCommand, Parameter[] parameters, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper, Map<String, AbstractValidator<T, ?>> localValidator, Map<String, AbstractGuardChecker<T>> localGuardChecker) {
CommandPart<T> first = null;
CommandPart<T> current = null;
for (String s : subCommand) {
CommandPart commandPart = new CommandPart(command, createMapper(s), null, null, null, null, help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND);
commandPart.setIgnoreAsArgument(true);
if (current != null) {
current.setNext(commandPart);
}
current = commandPart;
if (first == null) {
first = current;
}
}
for (int i = 1; i < parameters.length; i++) {
Parameter parameter = parameters[i];
AbstractTypeMapper<T, ?> typeMapper = getTypeMapper(parameter, localTypeMapper);
AbstractValidator<T, Object> validator = (AbstractValidator<T, Object>) getValidator(parameter, localValidator);
AbstractGuardChecker<T> guardChecker = getGuardChecker(parameter, localGuardChecker);
Class<?> varArgType = parameter.isVarArgs() ? parameter.getType().getComponentType() : null;
AbstractSWCommand.OptionalValue optionalValue = parameter.getAnnotation(AbstractSWCommand.OptionalValue.class);
CommandPart<T> commandPart = new CommandPart<>(command, typeMapper, validator, guardChecker, varArgType, optionalValue != null ? optionalValue.value() : null, help ? GuardCheckType.HELP_COMMAND : GuardCheckType.COMMAND);
if (current != null) {
current.setNext(commandPart);
}
current = commandPart;
if (first == null) {
first = current;
}
}
return first;
}
public static <T> AbstractTypeMapper<T, ?> getTypeMapper(Parameter parameter, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
Class<?> clazz = parameter.getType();
if (parameter.isVarArgs()) {
clazz = clazz.getComponentType();
}
AbstractSWCommand.ClassMapper classMapper = parameter.getAnnotation(AbstractSWCommand.ClassMapper.class);
AbstractSWCommand.Mapper mapper = parameter.getAnnotation(AbstractSWCommand.Mapper.class);
if (clazz.isEnum() && classMapper == null && mapper == null && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) {
return createEnumMapper((Class<Enum<?>>) clazz);
}
String name = clazz.getTypeName();
if (classMapper != null) {
name = classMapper.value().getTypeName();
} else if (mapper != null) {
name = mapper.value();
} else {
AbstractSWCommand.StaticValue staticValue = parameter.getAnnotation(AbstractSWCommand.StaticValue.class);
if (staticValue != null) {
if (parameter.getType() == String.class) {
return createMapper(staticValue.value());
}
if (staticValue.allowISE()) {
if ((parameter.getType() == boolean.class || parameter.getType() == Boolean.class) && staticValue.value().length == 2) {
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
return createMapper(s -> {
int index = tabCompletes.indexOf(s);
return index == -1 ? null : index != 0;
}, (commandSender, s) -> tabCompletes);
}
if ((parameter.getType() == int.class || parameter.getType() == Integer.class) && staticValue.value().length >= 2) {
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
return createMapper(s -> {
int index = tabCompletes.indexOf(s);
return index == -1 ? null : index;
}, (commandSender, s) -> tabCompletes);
}
if ((parameter.getType() == long.class || parameter.getType() == Long.class) && staticValue.value().length >= 2) {
List<String> tabCompletes = new ArrayList<>(Arrays.asList(staticValue.value()));
return createMapper(s -> {
long index = tabCompletes.indexOf(s);
return index == -1 ? null : index;
}, (commandSender, s) -> tabCompletes);
}
}
}
}
public static <T> AbstractTypeMapper<T, ?> getTypeMapper(String name, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
AbstractTypeMapper<T, ?> typeMapper = localTypeMapper.getOrDefault(name, (AbstractTypeMapper<T, ?>) MAPPER_FUNCTIONS.getOrDefault(name, null));
if (typeMapper == null) {
throw new IllegalArgumentException("No mapper found for " + name);
@ -167,79 +83,24 @@ public class SWCommandUtils {
return typeMapper;
}
public static <T> AbstractValidator<T, ?> getValidator(Parameter parameter, Map<String, AbstractValidator<T, ?>> localValidator) {
Class<?> clazz = parameter.getType();
AbstractSWCommand.ClassValidator classValidator = parameter.getAnnotation(AbstractSWCommand.ClassValidator.class);
if (classValidator != null) {
if (classValidator.value() != null) {
return getValidator(classValidator.value().getTypeName(), localValidator);
}
return getValidator(clazz.getTypeName(), localValidator);
}
AbstractSWCommand.Validator validator = parameter.getAnnotation(AbstractSWCommand.Validator.class);
if (validator != null) {
if (validator.value() != null && !validator.value().isEmpty()) {
return getValidator(validator.value(), localValidator);
}
return getValidator(clazz.getTypeName(), localValidator);
}
AbstractSWCommand.ErrorMessage errorMessage = parameter.getAnnotation(AbstractSWCommand.ErrorMessage.class);
if (errorMessage != null) {
return (AbstractValidator<T, Object>) (sender, value, messageSender) -> {
if (value == null) messageSender.send(errorMessage.value());
if (!errorMessage.allowEAs() && value != null && value.getClass().isArray() && Array.getLength(value) == 0) {
messageSender.send(errorMessage.value());
return false;
}
return value != null;
};
}
return null;
}
private static <T> AbstractValidator<T, ?> getValidator(String s, Map<String, AbstractValidator<T, ?>> localGuardChecker) {
AbstractValidator<T, ?> validator = localGuardChecker.getOrDefault(s, (AbstractValidator<T, ?>) VALIDATOR_FUNCTIONS.getOrDefault(s, null));
if (validator == null) {
throw new IllegalArgumentException("No validator found for " + s);
}
return validator;
}
@Deprecated
public static <T> AbstractGuardChecker<T> getGuardChecker(Parameter parameter, Map<String, AbstractGuardChecker<T>> localGuardChecker) {
public static <T> AbstractTypeMapper<T, ?> getTypeMapper(Parameter parameter, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
Class<?> clazz = parameter.getType();
if (parameter.isVarArgs()) {
clazz = clazz.getComponentType();
}
AbstractSWCommand.ClassGuard classGuard = parameter.getAnnotation(AbstractSWCommand.ClassGuard.class);
if (classGuard != null) {
if (classGuard.value() != null) {
return getGuardChecker(classGuard.value().getTypeName(), localGuardChecker);
if (clazz.isEnum() && !MAPPER_FUNCTIONS.containsKey(clazz.getTypeName()) && !localTypeMapper.containsKey(clazz.getTypeName())) {
return createEnumMapper((Class<Enum<?>>) clazz);
}
return getGuardChecker(clazz.getTypeName(), localGuardChecker);
return getTypeMapper(clazz.getTypeName(), localTypeMapper);
}
AbstractSWCommand.Guard guard = parameter.getAnnotation(AbstractSWCommand.Guard.class);
if (guard != null) {
if (guard.value() != null && !guard.value().isEmpty()) {
return getGuardChecker(guard.value(), localGuardChecker);
public static <T> AbstractValidator<T, ?> getValidator(AbstractSWCommand.Validator validator, Class<?> type, Map<String, AbstractValidator<T, ?>> localValidator) {
String s = validator.value() != null && !validator.value().isEmpty() ? validator.value() : type.getTypeName();
AbstractValidator<T, ?> concreteValidator = localValidator.getOrDefault(s, (AbstractValidator<T, ?>) VALIDATOR_FUNCTIONS.getOrDefault(s, null));
if (concreteValidator == null) {
throw new IllegalArgumentException("No validator found for " + s);
}
return getGuardChecker(clazz.getTypeName(), localGuardChecker);
}
return null;
}
@Deprecated
private static <T> AbstractGuardChecker<T> getGuardChecker(String s, Map<String, AbstractGuardChecker<T>> localGuardChecker) {
AbstractGuardChecker<T> guardChecker = localGuardChecker.getOrDefault(s, (AbstractGuardChecker<T>) GUARD_FUNCTIONS.getOrDefault(s, null));
if (guardChecker == null) {
throw new IllegalArgumentException("No guard found for " + s);
}
return guardChecker;
return concreteValidator;
}
public static <K, T> void addMapper(Class<T> clazz, AbstractTypeMapper<K, T> mapper) {
@ -258,16 +119,6 @@ public class SWCommandUtils {
VALIDATOR_FUNCTIONS.putIfAbsent(name, validator);
}
@Deprecated
public static <T> void addGuard(Class<?> clazz, AbstractGuardChecker<T> guardChecker) {
addGuard(clazz.getTypeName(), guardChecker);
}
@Deprecated
public static <T> void addGuard(String name, AbstractGuardChecker<T> guardChecker) {
GUARD_FUNCTIONS.putIfAbsent(name, guardChecker);
}
public static <T extends AbstractTypeMapper<K, String>, K> T createMapper(String... values) {
List<String> strings = Arrays.stream(values).map(String::toLowerCase).collect(Collectors.toList());
List<String> tabCompletes = Arrays.asList(values);
@ -306,10 +157,25 @@ public class SWCommandUtils {
};
}
private static Function<String, Collection<String>> numberCompleter(Function<String, ?> mapper) {
return s -> numberMapper(mapper).apply(s) != null
? Collections.singletonList(s)
: Collections.emptyList();
private static Function<String, Collection<String>> numberCompleter(Function<String, ?> mapper, boolean comma) {
return s -> {
if (numberMapper(mapper).apply(s) == null) {
return Collections.emptyList();
}
List<String> strings = new ArrayList<>();
if (s.length() == 0) {
strings.add("-");
} else {
strings.add(s);
}
for (int i = 0; i < 10; i++) {
strings.add(s + i);
}
if (comma && (!s.contains(".") || !s.contains(","))) {
strings.add(s + ".");
}
return strings;
};
}
static <T extends Annotation> T[] getAnnotation(Method method, Class<T> annotation) {

Datei anzeigen

@ -19,10 +19,10 @@
package de.steamwar.command;
import java.util.List;
import java.util.Collection;
import java.util.function.BiFunction;
import java.util.function.Function;
public interface SWTypeMapperCreator<T extends AbstractTypeMapper<K, V>, K, V> {
T createTypeMapper(Function<String, V> mapper, BiFunction<K, String, List<String>> tabCompleter);
T createTypeMapper(Function<String, V> mapper, BiFunction<K, String, Collection<String>> tabCompleter);
}

Datei anzeigen

@ -19,17 +19,20 @@
package de.steamwar.command;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
public class SubCommand<T> {
public class SubCommand<T> implements Comparable<SubCommand<T>> {
private AbstractSWCommand<T> abstractSWCommand;
Method method;
@ -38,31 +41,59 @@ public class SubCommand<T> {
private Predicate<T> senderPredicate;
private Function<T, ?> senderFunction;
AbstractValidator<T, T> validator;
AbstractGuardChecker<T> guardChecker;
boolean noTabComplete;
int comparableValue;
private Parameter[] parameters;
private CommandPart<T> commandPart;
SubCommand(AbstractSWCommand<T> abstractSWCommand, Method method, String[] subCommand, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper, Map<String, AbstractValidator<T, ?>> localValidator, Map<String, AbstractGuardChecker<T>> localGuardChecker, boolean help, String[] description, boolean noTabComplete) {
boolean isHelp = false;
SubCommand(AbstractSWCommand<T> abstractSWCommand, Method method, String[] subCommand, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper, Map<String, AbstractValidator<T, ?>> localValidator, String[] description, boolean noTabComplete) {
this.abstractSWCommand = abstractSWCommand;
this.method = method;
try {
this.method.setAccessible(true);
} catch (SecurityException e) {
throw new SecurityException(e.getMessage(), e);
}
this.subCommand = subCommand;
this.description = description;
this.noTabComplete = noTabComplete;
Parameter[] parameters = method.getParameters();
comparableValue = parameters[parameters.length - 1].isVarArgs() ? Integer.MAX_VALUE : -parameters.length;
parameters = method.getParameters();
AbstractSWCommand.Validator validator = parameters[0].getAnnotation(AbstractSWCommand.Validator.class);
if (validator != null) {
this.validator = (AbstractValidator<T, T>) SWCommandUtils.getValidator(validator, parameters[0].getType(), localValidator);
if (validator.invert()) {
AbstractValidator<T, T> current = this.validator;
this.validator = (sender, value, messageSender) -> !current.validate(sender, value, messageSender);
}
}
validator = (AbstractValidator<T, T>) SWCommandUtils.getValidator(parameters[0], localValidator);
guardChecker = SWCommandUtils.getGuardChecker(parameters[0], localGuardChecker);
commandPart = SWCommandUtils.generateCommandPart(abstractSWCommand, help, subCommand, parameters, localTypeMapper, localValidator, localGuardChecker);
commandPart = generateCommandPart(abstractSWCommand, subCommand, parameters, localTypeMapper, localValidator);
if (commandPart != null) isHelp = commandPart.isHelp();
senderPredicate = t -> parameters[0].getType().isAssignableFrom(t.getClass());
senderFunction = t -> parameters[0].getType().cast(t);
}
@Override
public int compareTo(SubCommand<T> o) {
int tLength = parameters.length + subCommand.length;
int oLength = o.parameters.length + o.subCommand.length;
boolean tVarArgs = parameters[parameters.length - 1].isVarArgs();
boolean oVarArgs = o.parameters[o.parameters.length - 1].isVarArgs();
if (tVarArgs) tLength *= -1;
if (oVarArgs) oLength *= -1;
if (tVarArgs && oVarArgs) return Integer.compare(tLength, oLength);
return -Integer.compare(tLength, oLength);
}
boolean invoke(Consumer<Runnable> errors, T sender, String alias, String[] args) {
try {
if (!senderPredicate.test(sender)) {
@ -77,21 +108,9 @@ public class SubCommand<T> {
if (!validator.validate(sender, sender, (s, objectArgs) -> {
abstractSWCommand.sendMessage(sender, s, objectArgs);
})) {
throw new CommandNoHelpException();
}
} else if (guardChecker != null) {
GuardResult guardResult = guardChecker.guard(sender, GuardCheckType.COMMAND, new String[0], null);
switch (guardResult) {
case ALLOWED:
break;
case DENIED:
throw new CommandNoHelpException();
case DENIED_WITH_HELP:
default:
return true;
return false;
}
}
method.setAccessible(true);
method.invoke(abstractSWCommand, senderFunction.apply(sender));
} else {
List<Object> objects = new ArrayList<>();
@ -100,26 +119,13 @@ public class SubCommand<T> {
if (!validator.validate(sender, sender, (s, objectArgs) -> {
abstractSWCommand.sendMessage(sender, s, objectArgs);
})) {
throw new CommandNoHelpException();
}
} else if (guardChecker != null) {
GuardResult guardResult = guardChecker.guard(sender, GuardCheckType.COMMAND, new String[0], null);
switch (guardResult) {
case ALLOWED:
break;
case DENIED:
throw new CommandNoHelpException();
case DENIED_WITH_HELP:
default:
return true;
return false;
}
}
commandPart.guardCheck(sender, args, 0);
objects.add(0, senderFunction.apply(sender));
method.setAccessible(true);
method.invoke(abstractSWCommand, objects.toArray());
}
} catch (CommandNoHelpException e) {
} catch (CommandFrameworkException e) {
throw e;
} catch (CommandParseException e) {
return false;
@ -132,20 +138,155 @@ public class SubCommand<T> {
}
List<String> tabComplete(T sender, String[] args) {
if (validator != null) {
if (!validator.validate(sender, sender, (s, objects) -> {
// ignore
})) {
return null;
}
} else if (guardChecker != null && guardChecker.guard(sender, GuardCheckType.TAB_COMPLETE, new String[0], null) != GuardResult.ALLOWED) {
if (validator != null && !validator.validate(sender, sender, (s, objects) -> {})) {
return null;
}
if (commandPart == null) {
return null;
}
List<String> list = new ArrayList<>();
commandPart.generateTabComplete(list, sender, args, 0);
commandPart.generateTabComplete(list, sender, args, new ArrayList<>(), 0);
return list;
}
private static <T> CommandPart<T> generateCommandPart(AbstractSWCommand<T> command, String[] subCommand, Parameter[] parameters, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper, Map<String, AbstractValidator<T, ?>> localValidator) {
CommandPart<T> first = null;
CommandPart<T> current = null;
for (String s : subCommand) {
CommandPart commandPart = new CommandPart(command, SWCommandUtils.createMapper(s), null, null, null, -1);
commandPart.addValidator(NULL_FILTER);
commandPart.setIgnoreAsArgument(true);
if (current != null) {
current.setNext(commandPart);
}
current = commandPart;
if (first == null) {
first = current;
}
}
for (int i = 1; i < parameters.length; i++) {
Parameter parameter = parameters[i];
AbstractTypeMapper<T, ?> typeMapper = handleImplicitTypeMapper(parameter, localTypeMapper);
Class<?> varArgType = parameter.isVarArgs() ? parameter.getType().getComponentType() : null;
AbstractSWCommand.OptionalValue optionalValue = parameter.getAnnotation(AbstractSWCommand.OptionalValue.class);
CommandPart<T> commandPart = new CommandPart<>(command, typeMapper, varArgType, optionalValue != null ? optionalValue.value() : null, parameter, i);
commandPart.setOnlyUseIfNoneIsGiven(optionalValue != null && optionalValue.onlyUINIG());
handleImplicitTypeValidator(parameter, commandPart, localValidator);
if (parameter.getAnnotation(AbstractSWCommand.AllowNull.class) == null) {
commandPart.addValidator((AbstractValidator<T, Object>) NULL_FILTER);
}
if (current != null) {
current.setNext(commandPart);
}
current = commandPart;
if (first == null) {
first = current;
}
}
return first;
}
private static <T> AbstractTypeMapper<T, Object> handleImplicitTypeMapper(Parameter parameter, Map<String, AbstractTypeMapper<T, ?>> localTypeMapper) {
Class<?> type = parameter.getType();
if (parameter.isVarArgs()) {
type = type.getComponentType();
}
Annotation[] annotations = parameter.getAnnotations();
Constructor<?> sourceConstructor = null;
Annotation sourceAnnotation = null;
List<Constructor<?>> parentConstructors = new ArrayList<>();
List<Annotation> parentAnnotations = new ArrayList<>();
for (Annotation annotation : annotations) {
CommandMetaData.ImplicitTypeMapper implicitTypeMapper = annotation.annotationType().getAnnotation(CommandMetaData.ImplicitTypeMapper.class);
if (implicitTypeMapper == null) continue;
Class<?> clazz = implicitTypeMapper.handler();
if (!AbstractTypeMapper.class.isAssignableFrom(clazz)) continue;
Constructor<?>[] constructors = clazz.getConstructors();
if (constructors.length != 1) continue;
Constructor<?> constructor = constructors[0];
if (needsTypeMapper(constructor)) {
parentConstructors.add(constructor);
parentAnnotations.add(annotation);
} else {
if (sourceAnnotation != null) {
throw new IllegalArgumentException("Multiple source type mappers found for parameter " + parameter);
}
sourceConstructor = constructor;
sourceAnnotation = annotation;
}
}
AbstractTypeMapper<T, Object> current;
if (sourceAnnotation != null) {
current = createInstance(sourceConstructor, sourceAnnotation, type, localTypeMapper);
} else {
current = (AbstractTypeMapper<T, Object>) SWCommandUtils.getTypeMapper(parameter, localTypeMapper);
}
for (int i = 0; i < parentConstructors.size(); i++) {
Constructor<?> constructor = parentConstructors.get(i);
Annotation annotation = parentAnnotations.get(i);
current = createInstance(constructor, annotation, type, localTypeMapper, current);
}
return current;
}
private static boolean needsTypeMapper(Constructor<?> constructor) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
if (AbstractTypeMapper.class.isAssignableFrom(parameterType)) {
return true;
}
}
return false;
}
private static <T> void handleImplicitTypeValidator(Parameter parameter, CommandPart<T> commandPart, Map<String, AbstractValidator<T, ?>> localValidator) {
Annotation[] annotations = parameter.getAnnotations();
Map<Integer, List<AbstractValidator<T, Object>>> validators = new HashMap<>();
for (Annotation annotation : annotations) {
CommandMetaData.ImplicitValidator implicitValidator = annotation.annotationType().getAnnotation(CommandMetaData.ImplicitValidator.class);
if (implicitValidator == null) continue;
Class<?> clazz = implicitValidator.handler();
if (!AbstractValidator.class.isAssignableFrom(clazz)) continue;
Constructor<?>[] constructors = clazz.getConstructors();
if (constructors.length != 1) continue;
AbstractValidator<T, Object> validator = createInstance(constructors[0], annotation, commandPart.getType(), localValidator);
validators.computeIfAbsent(implicitValidator.order(), integer -> new ArrayList<>()).add(validator);
}
List<Integer> keys = new ArrayList<>(validators.keySet());
keys.sort(Integer::compareTo);
for (Integer key : keys) {
List<AbstractValidator<T, Object>> list = validators.get(key);
for (AbstractValidator<T, Object> validator : list) {
commandPart.addValidator(validator);
}
}
}
private static <T> T createInstance(Constructor<?> constructor, Object... parameter) {
Class<?>[] types = constructor.getParameterTypes();
List<Object> objects = new ArrayList<>();
for (Class<?> clazz : types) {
boolean found = false;
for (Object o : parameter) {
if (clazz.isAssignableFrom(o.getClass())) {
objects.add(o);
found = true;
break;
}
}
if (!found) {
throw new RuntimeException("Could not find type " + clazz + " for constructor " + constructor);
}
}
try {
return (T) constructor.newInstance(objects.toArray());
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private static final AbstractValidator<?, Object> NULL_FILTER = (sender, value, messageSender) -> value != null;
}

Datei anzeigen

@ -24,6 +24,7 @@ import lombok.EqualsAndHashCode;
import lombok.experimental.UtilityClass;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
@UtilityClass
@ -36,35 +37,43 @@ public class TabCompletionCache {
void add(AbstractTypeMapper<?, ?> typeMapper, AbstractSWCommand.Cached cached) {
if (cached != null) {
TabCompletionCache.cached.add(typeMapper);
if (cached.global()) TabCompletionCache.global.add(typeMapper);
TabCompletionCache.cacheDuration.put(typeMapper, cached.timeUnit().toMillis(cached.cacheDuration()));
add(typeMapper, cached.global(), cached.cacheDuration(), cached.timeUnit());
}
}
public void add(AbstractTypeMapper<?, ?> typeMapper, boolean global, long cacheDuration, TimeUnit timeUnit) {
TabCompletionCache.cached.add(typeMapper);
if (global) TabCompletionCache.global.add(typeMapper);
TabCompletionCache.cacheDuration.put(typeMapper, timeUnit.toMillis(cacheDuration));
}
@EqualsAndHashCode
@AllArgsConstructor
private static class Key {
private Object sender;
private String arg;
private AbstractTypeMapper<?, ?> typeMapper;
}
@AllArgsConstructor
private static class TabCompletions {
private AbstractSWCommand<?> command;
private long timestamp;
private Collection<String> tabCompletions;
}
Collection<String> tabComplete(Object sender, AbstractTypeMapper<?, ?> typeMapper, AbstractSWCommand<?> command, Supplier<Collection<String>> tabCompleteSupplier) {
Collection<String> tabComplete(Object sender, String arg, AbstractTypeMapper<Object, ?> typeMapper, Supplier<Collection<String>> tabCompleteSupplier) {
if (!cached.contains(typeMapper)) return tabCompleteSupplier.get();
Key key = global.contains(typeMapper) ? new Key(null, typeMapper) : new Key(sender, typeMapper);
String normalizedArg = typeMapper.normalize(sender, arg);
if (normalizedArg == null) normalizedArg = "";
Key key = new Key(global.contains(typeMapper) ? null : sender, normalizedArg, typeMapper);
TabCompletions tabCompletions = tabCompletionCache.computeIfAbsent(key, ignore -> {
return new TabCompletions(command, System.currentTimeMillis(), tabCompleteSupplier.get());
return new TabCompletions(System.currentTimeMillis(), tabCompleteSupplier.get());
});
if (tabCompletions.command != command || System.currentTimeMillis() - tabCompletions.timestamp > cacheDuration.get(typeMapper)) {
tabCompletions = new TabCompletions(command, System.currentTimeMillis(), tabCompleteSupplier.get());
tabCompletionCache.put(key, tabCompletions);
if (System.currentTimeMillis() - tabCompletions.timestamp > cacheDuration.get(typeMapper)) {
tabCompletions.tabCompletions = tabCompleteSupplier.get();
}
tabCompletions.timestamp = System.currentTimeMillis();
return tabCompletions.tabCompletions;

Datei anzeigen

@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -17,13 +17,20 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
package de.steamwar.linkage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface AllowedContexts {
@Deprecated
@FunctionalInterface
public interface AbstractGuardChecker<T> {
/**
* While guarding the first parameter of the command the parameter s of this method is {@code null}
* The context in which this annotation is valid.
*/
GuardResult guard(T t, GuardCheckType guardCheckType, String[] previousArguments, String s);
Context[] value();
}

Datei anzeigen

@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -17,11 +17,9 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
package de.steamwar.linkage;
@Deprecated
public enum GuardResult {
ALLOWED,
DENIED_WITH_HELP,
DENIED
public enum Context {
BUNGEE,
SPIGOT
}

Datei anzeigen

@ -0,0 +1,44 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@AllowedContexts(Context.BUNGEE)
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface EventMode {
Mode value();
@AllArgsConstructor
enum Mode {
EventOnly(""),
NonEvent("!");
@Getter
private String prefix;
}
}

Datei anzeigen

@ -0,0 +1,352 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.FieldBuilder;
import de.steamwar.linkage.plan.MethodBuilder;
import de.steamwar.linkage.types.Plain_GENERIC;
import lombok.Cleanup;
import lombok.Getter;
import lombok.SneakyThrows;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import javax.tools.StandardLocation;
import java.io.*;
import java.lang.annotation.Annotation;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@SupportedAnnotationTypes("de.steamwar.linkage.Linked")
public class LinkageProcessor extends AbstractProcessor {
private static Context context;
@Getter
private static String pluginMain;
private String name;
private String className;
private Set<String> disabledFeatures = new HashSet<>();
private Messager messager;
private boolean processed = false;
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@SneakyThrows
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
name = new File(System.getProperty("user.dir")).getName().replaceAll("[^a-zA-Z]", "").toLowerCase();
messager = processingEnv.getMessager();
className = "LinkageUtils";
mainClass();
disabledFeatures();
}
@SneakyThrows
private void mainClass() {
File file = new File(System.getProperty("user.dir"));
Optional<File> pluginYMLFile = Files.walk(file.toPath())
.map(Path::toFile)
.filter(File::isFile)
.filter(f -> f.getName().equals("plugin.yml") || f.getName().equals("bungee.yml"))
.findFirst();
if (!pluginYMLFile.isPresent()) {
messager.printMessage(Diagnostic.Kind.ERROR, "Could not find plugin.yml or bungee.yml");
return;
}
context = pluginYMLFile.get().getName().equals("bungee.yml") ? Context.BUNGEE : Context.SPIGOT;
@Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(pluginYMLFile.get())));
Optional<String> mainName = reader.lines()
.filter(line -> line.startsWith("main:"))
.map(line -> line.substring(line.indexOf(':') + 1).trim())
.findFirst();
if (mainName.isPresent()) {
pluginMain = mainName.get();
} else {
messager.printMessage(Diagnostic.Kind.ERROR, "Could not find main class in plugin.yml or bungee.yml");
}
}
@SneakyThrows
private void disabledFeatures() {
File file = new File(System.getProperty("user.dir"), "disabled-features.txt");
if (!file.exists()) return;
@Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
reader.lines()
.map(String::trim)
.filter(line -> !line.isEmpty())
.filter(line -> !line.startsWith("#"))
.forEach(disabledFeatures::add);
}
@SneakyThrows
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (processed) return false;
processed = true;
Writer writer = processingEnv.getFiler().createSourceFile("de.steamwar." + name + ".linkage.LinkageUtils").openWriter();
BuildPlan buildPlan = new BuildPlan("de.steamwar." + name + ".linkage", className);
Set<TypeElement> disabledElements = new HashSet<>();
Set<TypeElement> elements = roundEnv.getElementsAnnotatedWith(Linked.class).stream()
.filter(element -> element.getKind() == ElementKind.CLASS)
.map(TypeElement.class::cast)
.peek(element -> {
String featureName = element.getAnnotation(Linked.class).feature();
if (featureName.isEmpty()) {
String tempName = element.getQualifiedName().toString();
if (tempName.contains(".features.")) {
tempName = tempName.substring(tempName.indexOf(".features.") + 10);
featureName = tempName.substring(0, tempName.indexOf('.'));
} else {
tempName = tempName.substring(0, tempName.lastIndexOf('.'));
featureName = tempName.substring(tempName.lastIndexOf('.') + 1);
}
}
if (disabledFeatures.contains(featureName) || disabledFeatures.contains("*")) {
disabledElements.add(element);
}
})
.peek(typeElement -> System.out.println("Found element: " + typeElement.getQualifiedName().toString()))
.collect(Collectors.toSet());
Map<Set<String>, List<TypeElement>> groupedByChecks = elements.stream()
.collect(Collectors.groupingBy(element -> checks(element, buildPlan)));
Map<String, TypeElement> neededFields = new HashMap<>();
Set<Runnable> fieldInjections = new HashSet<>();
for (TypeElement typeElement : elements) {
if (getLinkagesOfType(typeElement).size() > 1) {
neededFields.put(typeElement.getQualifiedName().toString(), typeElement);
}
List<VariableElement> variableElements = typeElement.getEnclosedElements().stream()
.filter(e -> e.getKind() == ElementKind.FIELD)
.map(VariableElement.class::cast)
.filter(e -> e.getAnnotation(LinkedInstance.class) != null)
.collect(Collectors.toList());
if (variableElements.isEmpty()) continue;
for (VariableElement variableElement : variableElements) {
if (!variableElement.getModifiers().contains(Modifier.PUBLIC)) {
messager.printMessage(Diagnostic.Kind.ERROR, "Field " + variableElement.getSimpleName() + " must be public", variableElement);
continue;
}
if (variableElement.getModifiers().contains(Modifier.STATIC)) {
messager.printMessage(Diagnostic.Kind.ERROR, "Field " + variableElement.getSimpleName() + " must be non static", variableElement);
continue;
}
if (variableElement.getModifiers().contains(Modifier.FINAL)) {
messager.printMessage(Diagnostic.Kind.ERROR, "Field " + variableElement.getSimpleName() + " must be non final", variableElement);
continue;
}
TypeElement fieldType = (TypeElement) ((DeclaredType) variableElement.asType()).asElement();
if (disabledElements.contains(fieldType)) {
continue;
}
if (disabledElements.contains(typeElement)) {
continue;
}
neededFields.put(typeElement.getQualifiedName().toString(), typeElement);
neededFields.put(fieldType.getQualifiedName().toString(), fieldType);
fieldInjections.add(() -> {
specialElements(typeElement, buildPlan, buildPlan::addStaticLine, () -> {
buildPlan.addStaticLine(getElement(typeElement, neededFields) + "." + variableElement.getSimpleName().toString() + " = " + getElement((TypeElement) ((DeclaredType) variableElement.asType()).asElement(), neededFields) + ";");
});
});
}
}
neededFields.forEach((s, typeElement) -> {
if (disabledElements.contains(typeElement)) return;
buildPlan.addImport(typeElement.getQualifiedName().toString());
String t = typeElement.getSimpleName().toString();
t = t.substring(0, 1).toLowerCase() + t.substring(1);
buildPlan.addField(new FieldBuilder(typeElement.getSimpleName().toString(), t));
String finalT = t;
specialElements(typeElement, buildPlan, buildPlan::addStaticLine, () -> {
buildPlan.addStaticLine(finalT + " = new " + typeElement.getSimpleName().toString() + "();");
});
});
fieldInjections.forEach(Runnable::run);
Map<String, MethodBuilder> methods = new HashMap<>();
for (Map.Entry<Set<String>, List<TypeElement>> entry : groupedByChecks.entrySet()) {
Map<String, Map<TypeElement, List<LinkageType>>> groupedByMethod = new HashMap<>();
for (TypeElement typeElement : entry.getValue()) {
for (Map.Entry<String, List<LinkageType>> linkages : getLinkagesOfType(typeElement).entrySet()) {
groupedByMethod.computeIfAbsent(linkages.getKey(), ignored -> new HashMap<>())
.put(typeElement, linkages.getValue());
}
}
for (Map.Entry<String, Map<TypeElement, List<LinkageType>>> group : groupedByMethod.entrySet()) {
MethodBuilder method = methods.computeIfAbsent(group.getKey(), s -> {
MethodBuilder methodBuilder = new MethodBuilder(s, "void");
buildPlan.addMethod(methodBuilder);
return methodBuilder;
});
boolean generated = false;
for (Map.Entry<TypeElement, List<LinkageType>> toGenerate : group.getValue().entrySet()) {
if (disabledElements.contains(toGenerate.getKey())) continue;
if (!generated && !entry.getKey().isEmpty()) {
method.addLine("if (" + String.join(" && ", entry.getKey()) + ") {");
generated = true;
}
TypeElement typeElement = toGenerate.getKey();
String instance = getElement(typeElement, neededFields);
if (toGenerate.getValue().size() > 1 && instance.startsWith("new ")) {
method.addLine(typeElement.getSimpleName() + " local" + typeElement.getSimpleName().toString() + " = " + instance + ";");
instance = "local" + typeElement.getSimpleName().toString();
}
String finalInstance = instance;
toGenerate.getValue().forEach(linkageType -> {
buildPlan.addImport(typeElement.getQualifiedName().toString());
linkageType.generateCode(buildPlan, method, finalInstance, typeElement);
});
}
if (generated && !entry.getKey().isEmpty()) method.addLine("}");
}
}
BufferedWriter bufferedWriter = new BufferedWriter(writer);
buildPlan.write(bufferedWriter);
bufferedWriter.close();
return true;
}
private String getElement(TypeElement typeElement, Map<String, TypeElement> neededFields) {
String s = typeElement.getSimpleName().toString();
if (neededFields.containsKey(typeElement.getQualifiedName().toString())) {
return s.substring(0, 1).toLowerCase() + s.substring(1);
}
return "new " + s + "()";
}
private Set<String> checks(TypeElement typeElement, BuildPlan buildPlan) {
Set<String> checks = new HashSet<>();
MinVersion minVersion = typeElement.getAnnotation(MinVersion.class);
MaxVersion maxVersion = typeElement.getAnnotation(MaxVersion.class);
EventMode eventMode = typeElement.getAnnotation(EventMode.class);
PluginCheck[] pluginChecks = typeElement.getAnnotationsByType(PluginCheck.class);
if (context == Context.SPIGOT) {
errorOnNonNull(typeElement, eventMode);
if (minVersion != null) {
buildPlan.addImport("de.steamwar.core.Core");
checks.add("Core.getVersion() >= " + minVersion.value());
}
if (maxVersion != null) {
buildPlan.addImport("de.steamwar.core.Core");
checks.add("Core.getVersion() <= " + maxVersion.value());
}
if (pluginChecks.length != 0) {
buildPlan.addImport("org.bukkit.Bukkit");
Arrays.stream(pluginChecks).map(pluginCheck -> {
return "Bukkit.getPluginManager().getPlugin(\"" + pluginCheck.value() + "\") " + (pluginCheck.has() == PluginCheck.Has.THIS ? "!" : "=") + "= null";
}).forEach(checks::add);
}
} else {
errorOnNonNull(typeElement, minVersion, maxVersion);
if (eventMode != null) {
buildPlan.addImport("de.steamwar.bungeecore.BungeeCore");
checks.add(eventMode.value().getPrefix() + "BungeeCore.EVENT_MODE");
}
if (pluginChecks.length != 0) {
buildPlan.addImport("net.md_5.bungee.BungeeCord");
Arrays.stream(pluginChecks).map(pluginCheck -> {
return "BungeeCord.getPluginManager().getPlugin(\"" + pluginCheck.value() + "\") " + (pluginCheck.has() == PluginCheck.Has.THIS ? "!" : "=") + "= null";
}).forEach(checks::add);
}
}
return checks;
}
private void specialElements(TypeElement typeElement, BuildPlan buildPlan, Consumer<String> stringConsumer, Runnable inner) {
Set<String> checks = checks(typeElement, buildPlan);
if (!checks.isEmpty()) stringConsumer.accept("if (" + String.join(" && ", checks) + ") {");
inner.run();
if (!checks.isEmpty()) stringConsumer.accept("}");
}
private void errorOnNonNull(TypeElement typeElement, Annotation... annotations) {
for (Annotation annotation : annotations) {
if (annotation != null) {
messager.printMessage(Diagnostic.Kind.ERROR, annotation.annotationType().getSimpleName() + " is not supported in " + context.name(), typeElement);
}
}
}
private Plain_GENERIC plain_GENERIC = new Plain_GENERIC();
private Map<String, List<LinkageType>> getLinkagesOfType(TypeElement typeElement) {
Map<String, List<LinkageType>> linkages = new HashMap<>();
Stream.concat(Stream.of(typeElement.getSuperclass()), typeElement.getInterfaces().stream())
.map(this::resolveSingle)
.filter(Objects::nonNull)
.forEach(linkageType -> linkages.computeIfAbsent(linkageType.method(), s -> new ArrayList<>()).add(linkageType));
if (linkages.size() == 1 && linkages.containsKey("unlink")) {
linkages.put(plain_GENERIC.method(), Collections.singletonList(plain_GENERIC));
}
return linkages;
}
private LinkageType resolveSingle(TypeMirror typeMirror) {
String qualifier = typeMirror.toString();
if (qualifier.contains("<")) qualifier = qualifier.substring(0, qualifier.indexOf('<'));
qualifier = qualifier.substring(qualifier.lastIndexOf('.') + 1);
try {
return (LinkageType) Class.forName("de.steamwar.linkage.types." + qualifier + "_" + context.name()).getDeclaredConstructor().newInstance();
} catch (Exception e) {
// Ignore
}
try {
return (LinkageType) Class.forName("de.steamwar.linkage.types." + qualifier + "_GENERIC").getDeclaredConstructor().newInstance();
} catch (Exception e) {
// Ignore
}
return null;
}
}

Datei anzeigen

@ -0,0 +1,36 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public interface LinkageType {
default String getPluginMain() {
return LinkageProcessor.getPluginMain();
}
String method();
void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement);
}

Datei anzeigen

@ -0,0 +1,28 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage;
import java.lang.annotation.*;
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface Linked {
String feature() default "";
}

Datei anzeigen

@ -0,0 +1,30 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.FIELD})
public @interface LinkedInstance {
}

Datei anzeigen

@ -0,0 +1,32 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@AllowedContexts(Context.SPIGOT)
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface MaxVersion {
int value();
}

Datei anzeigen

@ -0,0 +1,32 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@AllowedContexts(Context.SPIGOT)
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
public @interface MinVersion {
int value();
}

Datei anzeigen

@ -0,0 +1,42 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage;
import java.lang.annotation.*;
@AllowedContexts({Context.BUNGEE, Context.SPIGOT})
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
@Repeatable(PluginCheck.PluginChecks.class)
public @interface PluginCheck {
Has has() default Has.THIS;
String value();
enum Has {
THIS,
NOT
}
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE})
@interface PluginChecks {
@SuppressWarnings("unused") PluginCheck[] value() default {};
}
}

Datei anzeigen

@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -17,11 +17,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
package de.steamwar.linkage.api;
@Deprecated
public enum GuardCheckType {
COMMAND,
HELP_COMMAND,
TAB_COMPLETE
public interface Disable {
void disable();
}

Datei anzeigen

@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -17,9 +17,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
package de.steamwar.linkage.api;
class CommandNoHelpException extends RuntimeException {
CommandNoHelpException() {}
public interface Enable {
void enable();
}

Datei anzeigen

@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -17,9 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command.dto;
package de.steamwar.linkage.api;
import de.steamwar.command.AbstractGuardChecker;
public interface TestGuardChecker extends AbstractGuardChecker<String> {
public interface Plain {
}

Datei anzeigen

@ -0,0 +1,89 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage.plan;
import lombok.RequiredArgsConstructor;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.*;
@RequiredArgsConstructor
public class BuildPlan {
private final String packageName;
private Set<String> imports = new HashSet<>();
private final String className;
private List<FieldBuilder> fieldBuilders = new ArrayList<>();
private Map<String, MethodBuilder> methodBuilderMap = new HashMap<>();
private List<String> staticLines = new ArrayList<>();
public void addImport(String importName) {
imports.add(importName);
}
public void addField(FieldBuilder fieldBuilder) {
fieldBuilders.add(fieldBuilder);
}
public void addMethod(MethodBuilder methodBuilder) {
methodBuilderMap.put(methodBuilder.getMethodName(), methodBuilder);
}
public boolean hasMethod(String methodName) {
return methodBuilderMap.containsKey(methodName);
}
public void addStaticLine(String line) {
staticLines.add(line);
}
public void write(BufferedWriter writer) throws IOException {
writer.write("package " + packageName + ";\n");
if (!imports.isEmpty()) {
writer.write("\n");
for (String importName : imports) {
writer.write("import " + importName + ";\n");
}
}
writer.write("\n");
writer.write("public class " + className + " {\n");
if (!fieldBuilders.isEmpty()) {
for (FieldBuilder fieldBuilder : fieldBuilders) {
fieldBuilder.write(writer);
}
writer.write("\n");
}
if (!staticLines.isEmpty()) {
writer.write(" static {\n");
for (String line : staticLines) {
writer.write(" " + line + "\n");
}
writer.write(" }\n");
writer.write("\n");
}
for (MethodBuilder methodBuilder : methodBuilderMap.values()) {
methodBuilder.write(writer);
writer.write("\n");
}
writer.write("}\n");
}
}

Datei anzeigen

@ -0,0 +1,44 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage.plan;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.io.BufferedWriter;
import java.io.IOException;
@RequiredArgsConstructor
@AllArgsConstructor
public class FieldBuilder {
@Getter
private final String type;
private final String name;
private String initializer;
public String getFieldName() {
return name;
}
public void write(BufferedWriter writer) throws IOException {
writer.write(" private static " + type + " " + getFieldName() + (initializer == null ? "" : " = " + initializer) + ";\n");
}
}

Datei anzeigen

@ -0,0 +1,70 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage.plan;
import lombok.RequiredArgsConstructor;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@RequiredArgsConstructor
public class MethodBuilder {
private final String name;
private final String returnType;
private List<ParameterBuilder> parameters = new ArrayList<>();
private List<String> lines = new ArrayList<>();
private boolean isPrivate = false;
public void addParameter(ParameterBuilder parameterBuilder) {
parameters.add(parameterBuilder);
}
public void addLine(String line) {
lines.add(line);
}
public String getMethodName() {
return name;
}
public void setPrivate(boolean isPrivate) {
this.isPrivate = isPrivate;
}
public void write(BufferedWriter writer) throws IOException {
writer.write(" " + (isPrivate ? "private" : "public") + " static " + returnType + " " + getMethodName() + "(");
for (int i = 0; i < parameters.size(); i++) {
parameters.get(i).write(writer);
if (i < parameters.size() - 1) {
writer.write(", ");
}
}
writer.write(") {");
for (String line : lines) {
writer.write("\n");
writer.write(" " + line);
}
writer.write("\n");
writer.write(" }\n");
}
}

Datei anzeigen

@ -0,0 +1,35 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage.plan;
import lombok.AllArgsConstructor;
import java.io.BufferedWriter;
import java.io.IOException;
@AllArgsConstructor
public class ParameterBuilder {
private String type;
private String name;
public void write(BufferedWriter writer) throws IOException {
writer.write(type + " " + name);
}
}

Datei anzeigen

@ -0,0 +1,39 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class Disable_GENERIC implements LinkageType {
@Override
public String method() {
return "unlink";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
method.addLine(instance + ".disable();");
}
}

Datei anzeigen

@ -0,0 +1,39 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class Enable_GENERIC implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
method.addLine(instance + ".enable();");
}
}

Datei anzeigen

@ -0,0 +1,41 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class Listener_BUNGEE implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
buildPlan.addImport("net.md_5.bungee.api.ProxyServer");
buildPlan.addImport("de.steamwar.bungeecore.BungeeCore");
method.addLine("ProxyServer.getInstance().getPluginManager().registerListener(BungeeCore.get(), " + instance + ");");
}
}

Datei anzeigen

@ -0,0 +1,105 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.FieldBuilder;
import de.steamwar.linkage.plan.MethodBuilder;
import de.steamwar.linkage.plan.ParameterBuilder;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import java.util.HashMap;
import java.util.Map;
public class Listener_SPIGOT implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
Map<String, TypeElement> eventClasses = new HashMap<>();
Map<TypeElement, ExecutableElement> eventMethods = new HashMap<>();
typeElement.getEnclosedElements().stream().filter(e -> e.getKind() == ElementKind.METHOD).map(ExecutableElement.class::cast).filter(e -> {
return e.getAnnotationMirrors().stream().anyMatch(annotationMirror -> {
return annotationMirror.getAnnotationType().asElement().getSimpleName().toString().equals("EventHandler");
});
}).forEach(e -> {
TypeElement current = ((TypeElement)((DeclaredType) e.getParameters().get(0).asType()).asElement());
eventClasses.put(current.getQualifiedName().toString(), current);
eventMethods.put(current, e);
});
eventClasses.forEach((s, eventType) -> {
if (buildPlan.hasMethod(eventType.getSimpleName().toString())) return;
buildPlan.addImport("org.bukkit.event.HandlerList");
buildPlan.addImport("org.bukkit.event.Listener");
buildPlan.addImport("java.util.function.Consumer");
buildPlan.addImport("org.bukkit.event.EventPriority");
buildPlan.addImport("org.bukkit.plugin.RegisteredListener");
buildPlan.addImport("org.bukkit.plugin.EventExecutor");
buildPlan.addImport(s);
buildPlan.addField(new FieldBuilder("HandlerList", "handlerList" + eventType.getSimpleName()));
MethodBuilder methodBuilder = new MethodBuilder(eventType.getSimpleName().toString(), "void");
methodBuilder.addParameter(new ParameterBuilder("Listener", "listener"));
methodBuilder.addParameter(new ParameterBuilder("Consumer<" + eventType.getSimpleName() + ">", "consumer"));
methodBuilder.addParameter(new ParameterBuilder("EventPriority", "eventPriority"));
methodBuilder.addParameter(new ParameterBuilder("boolean", "ignoreCancelled"));
methodBuilder.setPrivate(true);
methodBuilder.addLine("EventExecutor eventExecutor = (l, event) -> {");
methodBuilder.addLine(" if (event instanceof " + eventType.getSimpleName() + ") {");
methodBuilder.addLine(" consumer.accept((" + eventType.getSimpleName() + ") event);");
methodBuilder.addLine(" }");
methodBuilder.addLine("};");
methodBuilder.addLine("handlerList" + eventType.getSimpleName() + ".register(new RegisteredListener(listener, eventExecutor, eventPriority, " + getPluginMain() + ".getInstance(), ignoreCancelled));");
buildPlan.addMethod(methodBuilder);
method.addLine("handlerList" + eventType.getSimpleName() + " = " + eventType.getSimpleName() + ".getHandlerList();");
});
String localInstance = "local" + typeElement.getSimpleName().toString();
if (!instance.startsWith("new ")) {
localInstance = instance;
} else {
method.addLine(typeElement.getSimpleName() + " " + localInstance + " = " + instance + ";");
}
String finalLocalInstance = localInstance;
eventMethods.forEach((type, executableElement) -> {
AnnotationMirror eventHandler = executableElement.getAnnotationMirrors().stream().filter(annotationMirror -> annotationMirror.getAnnotationType().asElement().getSimpleName().toString().equals("EventHandler")).findFirst().orElse(null);
if (eventHandler == null) {
return;
}
String priority = "NORMAL";
String ignoreCancelled = "false";
for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : eventHandler.getElementValues().entrySet()) {
if (entry.getKey().getSimpleName().toString().equals("priority")) {
priority = entry.getValue().getValue().toString();
} else if (entry.getKey().getSimpleName().toString().equals("ignoreCancelled")) {
ignoreCancelled = entry.getValue().getValue().toString();
}
}
method.addLine(type.getSimpleName().toString() + "(" + finalLocalInstance + ", " + finalLocalInstance + "::" + executableElement.getSimpleName().toString() + ", EventPriority." + priority + ", " + ignoreCancelled + ");");
});
}
}

Datei anzeigen

@ -0,0 +1,39 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class PacketHandler_GENERIC implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
method.addLine(instance + ".register();");
}
}

Datei anzeigen

@ -0,0 +1,39 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class Plain_GENERIC implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
if (instance.startsWith("new ")) method.addLine(instance + ";");
}
}

Datei anzeigen

@ -0,0 +1,39 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class SWCommand_BUNGEE implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
method.addLine(instance + ";");
}
}

Datei anzeigen

@ -0,0 +1,39 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.linkage.types;
import de.steamwar.linkage.LinkageType;
import de.steamwar.linkage.plan.BuildPlan;
import de.steamwar.linkage.plan.MethodBuilder;
import javax.lang.model.element.TypeElement;
public class SWCommand_SPIGOT implements LinkageType {
@Override
public String method() {
return "link";
}
@Override
public void generateCode(BuildPlan buildPlan, MethodBuilder method, String instance, TypeElement typeElement) {
method.addLine(instance + ".setMessage(" + getPluginMain() + ".MESSAGE);");
}
}

Datei anzeigen

@ -21,7 +21,6 @@ package de.steamwar.network.packets.client;
import de.steamwar.network.packets.NetworkPacket;
import lombok.EqualsAndHashCode;
import lombok.Getter;
@EqualsAndHashCode(callSuper = true)
public class ImALobbyPacket extends NetworkPacket {

Datei anzeigen

@ -0,0 +1,39 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.network.packets.client;
import de.steamwar.network.packets.NetworkPacket;
import lombok.*;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
@Getter
public class RequestSchematicSearchPacket extends NetworkPacket {
private static final long serialVersionUID = -6525229932332581648L;
private int playerId;
private int schematicId;
private boolean ignoreBlockData;
private boolean ignoreAir;
private boolean airAsAny;
}

Datei anzeigen

@ -38,4 +38,5 @@ public class FightEndsPacket extends NetworkPacket {
private List<Integer> bluePlayers;
private List<Integer> redPlayers;
private String gameMode;
private int duration;
}

Datei anzeigen

@ -0,0 +1,67 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.List;
@AllArgsConstructor
public class BannedUserIPs {
private static final Table<BannedUserIPs> table = new Table<>(BannedUserIPs.class);
private static final SelectStatement<BannedUserIPs> getByID = table.selectFields("UserID");
private static final SelectStatement<BannedUserIPs> getByIP = new SelectStatement<>(table, "SELECT * FROM BannedUserIPs WHERE IP = ? ORDER BY Timestamp DESC");
private static final Statement banIP = table.insertAll();
private static final Statement unbanIPs = table.deleteFields("UserID");
@Getter
@Field(keys = {Table.PRIMARY})
private final int userID;
@Getter
@Field(def = "CURRENT_TIMESTAMP")
private final Timestamp timestamp;
@Field(keys = {Table.PRIMARY})
private final String ip;
public static List<BannedUserIPs> get(int userID) {
return getByID.listSelect(userID);
}
public static List<BannedUserIPs> get(String ip) {
return getByIP.listSelect(ip);
}
public static void banIP(int userID, String ip){
banIP.update(userID, Timestamp.from(Instant.now()), ip);
}
public static void unbanIPs(int userID) {
unbanIPs.update(userID);
}
}

Datei anzeigen

@ -0,0 +1,123 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.Getter;
import java.util.*;
public class BauweltMember {
private static final Map<Integer, BauweltMember> memberCache = new HashMap<>();
public static void clear() {
memberCache.clear();
}
private static final Table<BauweltMember> table = new Table<>(BauweltMember.class);
private static final SelectStatement<BauweltMember> getMember = table.select(Table.PRIMARY);
private static final SelectStatement<BauweltMember> getMembers = table.selectFields("BauweltID");
private static final Statement update = table.insertAll();
private static final Statement delete = table.delete(Table.PRIMARY);
public static void addMember(UUID ownerID, UUID memberID) {
new BauweltMember(SteamwarUser.get(ownerID).getId(), SteamwarUser.get(memberID).getId(), false, false).updateDB();
}
public static BauweltMember getBauMember(UUID ownerID, UUID memberID){
return getBauMember(SteamwarUser.get(ownerID).getId(), SteamwarUser.get(memberID).getId());
}
public static BauweltMember getBauMember(int ownerID, int memberID){
BauweltMember member = memberCache.get(memberID);
if(member != null && member.bauweltID == ownerID)
return member;
return getMember.select(ownerID, memberID);
}
public static List<BauweltMember> getMembers(UUID bauweltID){
return getMembers(SteamwarUser.get(bauweltID).getId());
}
public static List<BauweltMember> getMembers(int bauweltID){
return getMembers.listSelect(bauweltID);
}
@Getter
@Field(keys = {Table.PRIMARY})
private final int bauweltID;
@Getter
@Field(keys = {Table.PRIMARY})
private final int memberID;
@Getter
@Field(def = "0")
private boolean worldEdit;
@Getter
@Field(def = "0")
private boolean world;
public BauweltMember(int bauweltID, int memberID, boolean worldEdit, boolean world) {
this.bauweltID = bauweltID;
this.memberID = memberID;
this.worldEdit = worldEdit;
this.world = world;
memberCache.put(memberID, this);
}
public void setWorldEdit(boolean worldEdit) {
this.worldEdit = worldEdit;
updateDB();
}
public void setWorld(boolean world) {
this.world = world;
updateDB();
}
private void updateDB(){
update.update(bauweltID, memberID, worldEdit, world);
}
public void remove(){
delete.update(bauweltID, memberID);
memberCache.remove(memberID);
}
public boolean isBuild() {
return worldEdit;
}
public boolean isSupervisor() {
return world;
}
public void setBuild(boolean build) {
this.worldEdit = build;
updateDB();
}
public void setSupervisor(boolean supervisor) {
this.world = supervisor;
updateDB();
}
}

Datei anzeigen

@ -0,0 +1,86 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.sql.Timestamp;
import java.util.List;
@AllArgsConstructor
public class CheckedSchematic {
private static final Table<CheckedSchematic> table = new Table<>(CheckedSchematic.class);
private static final SelectStatement<CheckedSchematic> statusOfNode = new SelectStatement<>(table, "SELECT * FROM CheckedSchematic WHERE NodeId = ? AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC");
private static final SelectStatement<CheckedSchematic> nodeHistory = new SelectStatement<>(table, "SELECT * FROM CheckedSchematic WHERE NodeId = ? AND DeclineReason != '' AND DeclineReason != 'Prüfvorgang abgebrochen' ORDER BY EndTime DESC");
private static final Statement insert = table.insertAll();
public static void create(int nodeId, String name, int owner, int validator, Timestamp startTime, Timestamp endTime, String reason){
insert.update(nodeId, owner, name, validator, startTime, endTime, reason);
}
public static void create(SchematicNode node, int validator, Timestamp startTime, Timestamp endTime, String reason){
create(node.getId(), node.getName(), node.getOwner(), validator, startTime, endTime, reason);
}
public static List<CheckedSchematic> getLastDeclinedOfNode(int node){
return statusOfNode.listSelect(node);
}
public static List<CheckedSchematic> previousChecks(SchematicNode node) {
return nodeHistory.listSelect(node.getId());
}
@Field(nullable = true)
private final Integer nodeId;
@Field
private final int nodeOwner;
@Field
private final String nodeName;
@Getter
@Field
private final int validator;
@Getter
@Field
private final Timestamp startTime;
@Getter
@Field
private final Timestamp endTime;
@Getter
@Field
private final String declineReason;
public int getNode() {
return nodeId;
}
public String getSchemName() {
return nodeName;
}
public int getSchemOwner() {
return nodeOwner;
}
}

109
src/de/steamwar/sql/Event.java Normale Datei
Datei anzeigen

@ -0,0 +1,109 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.List;
@AllArgsConstructor
public class Event {
static {
SchematicType.Normal.name(); // Ensure SchematicType is loaded.
}
private static final Table<Event> table = new Table<>(Event.class);
private static final SelectStatement<Event> byCurrent = new SelectStatement<>(table, "SELECT * FROM Event WHERE Start < now() AND End > now()");
private static final SelectStatement<Event> byId = table.select(Table.PRIMARY);
private static final SelectStatement<Event> byName = table.select("eventName");
private static final SelectStatement<Event> byComing = new SelectStatement<>(table, "SELECT * FROM Event WHERE Start > now()");
private static Event current = null;
public static Event get(){
if(current != null && current.now())
return current;
current = byCurrent.select();
return current;
}
public static Event get(int eventID){
return byId.select(eventID);
}
public static Event get(String eventName) {
return byName.select(eventName);
}
public static List<Event> getComing() {
return byComing.listSelect();
}
@Getter
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int eventID;
@Getter
@Field(keys = {"eventName"})
private final String eventName;
@Getter
@Field
private final Timestamp deadline;
@Getter
@Field
private final Timestamp start;
@Getter
@Field
private final Timestamp end;
@Getter
@Field
private final int maximumTeamMembers;
@Field(nullable = true)
private final SchematicType schemType;
@Field
private final boolean publicSchemsOnly;
@Deprecated
@Field
private final boolean spectateSystem;
public boolean publicSchemsOnly() {
return publicSchemsOnly;
}
public boolean spectateSystem(){
return spectateSystem;
}
public SchematicType getSchematicType() {
return schemType;
}
private boolean now() {
Instant now = Instant.now();
return now.isAfter(start.toInstant()) && now.isBefore(end.toInstant());
}
}

Datei anzeigen

@ -0,0 +1,136 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.sql.Timestamp;
import java.util.*;
import static java.time.temporal.ChronoUnit.SECONDS;
@AllArgsConstructor
public class EventFight implements Comparable<EventFight> {
private static final Table<EventFight> table = new Table<>(EventFight.class);
private static final SelectStatement<EventFight> byId = table.select(Table.PRIMARY);
private static final SelectStatement<EventFight> allComing = new SelectStatement<>(table, "SELECT * FROM EventFight WHERE StartTime > now() ORDER BY StartTime ASC");
private static final SelectStatement<EventFight> event = new SelectStatement<>(table, "SELECT * FROM EventFight WHERE EventID = ? ORDER BY StartTime ASC");
private static final Statement reschedule = table.update(Table.PRIMARY, "StartTime");
private static final Statement setResult = table.update(Table.PRIMARY, "Ergebnis");
private static final Statement setFight = table.update(Table.PRIMARY, "Fight");
private static final Queue<EventFight> fights = new PriorityQueue<>();
public static EventFight get(int fightID) {
return byId.select(fightID);
}
public static void loadAllComingFights() {
fights.clear();
fights.addAll(allComing.listSelect());
}
public static List<EventFight> getEvent(int eventID) {
return event.listSelect(eventID);
}
public static Queue<EventFight> getFights() {
return fights;
}
@Getter
@Field
private final int eventID;
@Getter
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int fightID;
@Getter
@Field
private Timestamp startTime;
@Getter
@Field
private final String spielmodus;
@Getter
@Field
private final String map;
@Getter
@Field
private final int teamBlue;
@Getter
@Field
private final int teamRed;
@Getter
@Field
@Deprecated
private final int kampfleiter;
@Getter
@Field
private final int spectatePort;
@Getter
@Field(def = "0")
private int ergebnis;
@Field(nullable = true)
private int fight;
public void setErgebnis(int winner) {
this.ergebnis = winner;
setResult.update(winner, fightID);
}
public void setFight(int fight) {
//Fight.FightID, not EventFight.FightID
this.fight = fight;
setFight.update(fight, fightID);
}
public boolean hasFinished() {
return fight != 0 || ergebnis != 0;
}
public void reschedule() {
startTime = Timestamp.from(new Date().toInstant().plus(30, SECONDS));
reschedule.update(startTime, fightID);
}
@Override
public int hashCode(){
return fightID;
}
@Override
public boolean equals(Object o){
if(o == null)
return false;
if(!(o instanceof EventFight))
return false;
return fightID == ((EventFight) o).fightID;
}
@Override
public int compareTo(EventFight o) {
return startTime.compareTo(o.startTime);
}
}

124
src/de/steamwar/sql/Fight.java Normale Datei
Datei anzeigen

@ -0,0 +1,124 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@AllArgsConstructor
public class Fight {
private static final Table<Fight> table = new Table<>(Fight.class);
private static final SelectStatement<Fight> getPage = new SelectStatement<>(table, "SELECT f.*, (b.NodeId IS NULL OR b.AllowReplay) AND (r.NodeId IS NULL OR r.AllowReplay) AS ReplayAllowed, (SELECT COUNT(1) FROM Replay WHERE Replay.FightID = f.FightID) as ReplayAvailable FROM Fight f LEFT OUTER JOIN SchematicNode b ON f.BlueSchem = b.NodeId LEFT OUTER JOIN SchematicNode r ON f.RedSchem = r.NodeId ORDER BY FightID DESC LIMIT ?, ?");
private static final Statement insert = table.insertFields(true, "GameMode", "Server", "StartTime", "Duration", "BlueLeader", "RedLeader", "BlueSchem", "RedSchem", "Win", "WinCondition");
public static List<Fight> getPage(int page, int elementsPerPage) {
List<Fight> fights = getPage.listSelect(page * elementsPerPage, elementsPerPage);
List<FightPlayer> fightPlayers = FightPlayer.batchGet(fights.stream().map(f -> f.fightID));
for(Fight fight : fights) {
fight.initPlayers(fightPlayers);
}
SteamwarUser.batchCache(fightPlayers.stream().map(FightPlayer::getUserID).collect(Collectors.toSet()));
return fights;
}
public static int create(String gamemode, String server, Timestamp starttime, int duration, int blueleader, int redleader, Integer blueschem, Integer redschem, int win, String wincondition){
return insert.insertGetKey(gamemode, server, starttime, duration, blueleader, redleader, blueschem, redschem, win, wincondition);
}
@Getter
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int fightID;
@Field
private final String gameMode;
@Getter
@Field
private final String server;
@Getter
@Field
private final Timestamp startTime;
@Field
private final int duration;
@Field
private final int blueLeader;
@Field
private final int redLeader;
@Field(nullable = true)
private final Integer blueSchem;
@Field(nullable = true)
private final Integer redSchem;
@Getter
@Field
private final int win;
@Field
private final String wincondition;
@Field // Virtual field for easy select
private final boolean replayAllowed;
@Field // Virtual field for easy select
private final boolean replayAvailable;
@Getter
private final List<FightPlayer> bluePlayers = new ArrayList<>();
@Getter
private final List<FightPlayer> redPlayers = new ArrayList<>();
public SchematicType getSchemType() {
return SchematicType.fromDB(gameMode);
}
public SteamwarUser getBlueLeader() {
return SteamwarUser.get(blueLeader);
}
public SteamwarUser getRedLeader() {
return SteamwarUser.get(redLeader);
}
public boolean replayAllowed() {
return replayExists() && replayAllowed;
}
public boolean replayExists() {
return getSchemType() != null && replayAvailable;
}
private void initPlayers(List<FightPlayer> fightPlayers) {
for(FightPlayer fp : fightPlayers) {
if(fp.getFightID() != fightID)
continue;
if(fp.getTeam() == 1)
bluePlayers.add(fp);
else
redPlayers.add(fp);
}
}
}

Datei anzeigen

@ -0,0 +1,65 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@AllArgsConstructor
public class FightPlayer {
private static final Table<FightPlayer> table = new Table<>(FightPlayer.class);
private static final Statement create = table.insertAll();
private static final SelectStatement<FightPlayer> batchGet = new SelectStatement<>(table, "SELECT * FROM FightPlayer WHERE FightID IN ?");
@Getter
@Field(keys = {Table.PRIMARY})
private final int fightID;
@Getter
@Field(keys = {Table.PRIMARY})
private final int userID;
@Getter
@Field
private final int team;
@Field
private final String kit;
@Field
private final int kills;
@Field
private final boolean isOut;
public static void create(int fightID, int userID, boolean blue, String kit, int kills, boolean isOut) {
create.update(fightID, userID, blue ? 1 : 2, kit, kills, isOut);
}
public static List<FightPlayer> batchGet(Stream<Integer> fightIds) {
try (SelectStatement<FightPlayer> batch = new SelectStatement<>(table, "SELECT * FROM FightPlayer WHERE FightID IN (" + fightIds.map(Object::toString).collect(Collectors.joining(", ")) + ")")) {
return batch.listSelect();
}
}
}

Datei anzeigen

@ -0,0 +1,59 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.sql.ResultSet;
import java.util.UUID;
@AllArgsConstructor
public class IgnoreSystem {
private static final Table<IgnoreSystem> table = new Table<>(IgnoreSystem.class, "IgnoredPlayers");
private static final SelectStatement<IgnoreSystem> select = table.select(Table.PRIMARY);
private static final Statement insert = table.insertAll();
private static final Statement delete = table.delete(Table.PRIMARY);
@Field(keys = {Table.PRIMARY})
private final int ignorer;
@Field(keys = {Table.PRIMARY})
private final int ignored;
public static boolean isIgnored(UUID ignorer, UUID ignored){
return isIgnored(SteamwarUser.get(ignorer), SteamwarUser.get(ignored));
}
public static boolean isIgnored(SteamwarUser ignorer, SteamwarUser ignored) {
return select.select(ResultSet::next, ignorer.getId(), ignored.getId());
}
public static void ignore(SteamwarUser ignorer, SteamwarUser ignored) {
insert.update(ignorer.getId(), ignored.getId());
}
public static void unIgnore(SteamwarUser ignorer, SteamwarUser ignored) {
delete.update(ignorer.getId(), ignored.getId());
}
}

101
src/de/steamwar/sql/Mod.java Normale Datei
Datei anzeigen

@ -0,0 +1,101 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@AllArgsConstructor
public class Mod {
static {
SqlTypeMapper.ordinalEnumMapper(Platform.class);
SqlTypeMapper.ordinalEnumMapper(ModType.class);
}
private static final Table<Mod> table = new Table<>(Mod.class, "Mods");
private static final SelectStatement<Mod> get = table.select(Table.PRIMARY);
private static final SelectStatement<Mod> findFirst = new SelectStatement<>(table, "SELECT * FROM Mods WHERE ModType = 0 LIMIT 1");
private static final SelectStatement<Mod> getPageOfType = new SelectStatement<>(table, "SELECT * FROM Mods WHERE ModType = ? ORDER BY ModName DESC LIMIT ?, ?");
private static final Statement insert = table.insert(Table.PRIMARY);
private static final Statement set = table.update(Table.PRIMARY, "ModType");
public static Mod get(String name, Platform platform) {
return get.select(platform, name);
}
public static Mod getOrCreate(String name, Platform platform) {
Mod mod = get(name, platform);
if(mod != null)
return mod;
insert.update(platform, name);
return new Mod(platform, name, ModType.UNKLASSIFIED);
}
public static List<Mod> getAllModsFiltered(int page, int elementsPerPage, Mod.ModType filter) {
return Mod.getPageOfType.listSelect(filter, page * elementsPerPage, elementsPerPage);
}
public static Mod findFirstMod() {
return findFirst.select();
}
@Getter
@Field(keys = {Table.PRIMARY})
private final Platform platform;
@Getter
@Field(keys = {Table.PRIMARY})
private final String modName;
@Getter
@Field(def = "0")
private ModType modType;
public void setModType(Mod.ModType modType) {
set.update(modType, platform, modName);
this.modType = modType;
}
public enum Platform {
FORGE,
LABYMOD,
FABRIC
}
public enum ModType {
UNKLASSIFIED("7"),
GREEN("a"),
YELLOW("e"),
RED("c"),
YOUTUBER_ONLY("6");
ModType(String colorcode) {
this.colorcode = colorcode;
}
private final String colorcode;
public String getColorCode() {
return colorcode;
}
}
}

Datei anzeigen

@ -0,0 +1,23 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
public class NoClipboardException extends RuntimeException {
}

Datei anzeigen

@ -0,0 +1,92 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.io.*;
import java.sql.PreparedStatement;
import java.util.zip.GZIPInputStream;
@AllArgsConstructor
public class NodeData {
static {
new SqlTypeMapper<>(PipedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("PipedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
new SqlTypeMapper<>(ByteArrayInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("ByteArrayInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
new SqlTypeMapper<>(BufferedInputStream.class, "BLOB", (rs, identifier) -> { throw new SecurityException("BufferedInputStream is write only datatype"); }, PreparedStatement::setBinaryStream);
}
private static final Table<NodeData> table = new Table<>(NodeData.class);
private static final Statement updateDatabase = new Statement("INSERT INTO NodeData(NodeId, NodeFormat, SchemData) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE NodeFormat = VALUES(NodeFormat), SchemData = VALUES(SchemData)");
private static final Statement selSchemData = new Statement("SELECT SchemData FROM NodeData WHERE NodeId = ?");
private static final SelectStatement<NodeData> get = table.select(Table.PRIMARY);
public static NodeData get(SchematicNode node) {
if(node.isDir())
throw new IllegalArgumentException("Node is a directory");
return get.select(rs -> {
if(rs.next()) {
return new NodeData(node.getId(), rs.getBoolean("NodeFormat"));
} else {
return new NodeData(node.getId(), false);
}
}, node);
}
@Getter
@Field(keys = {Table.PRIMARY})
private final int nodeId;
@Field
private boolean nodeFormat;
public InputStream schemData() throws IOException {
try {
return selSchemData.select(rs -> {
rs.next();
InputStream schemData = rs.getBinaryStream("SchemData");
try {
if(rs.wasNull() || schemData.available() == 0) {
throw new SecurityException("SchemData is null");
}
return new GZIPInputStream(schemData);
} catch (IOException e) {
throw new SecurityException("SchemData is wrong", e);
}
}, nodeId);
} catch (Exception e) {
throw new IOException(e);
}
}
public void saveFromStream(InputStream blob, boolean newFormat) {
updateDatabase.update(nodeId, newFormat, blob);
nodeFormat = newFormat;
}
public boolean getNodeFormat() {
return nodeFormat;
}
}

Datei anzeigen

@ -0,0 +1,69 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2021 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Timestamp;
import java.time.Instant;
@AllArgsConstructor
public class NodeDownload {
private static final char[] HEX = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
private static final String LINK_BASE = "https://steamwar.de/download.php?schem=";
private static final Table<NodeDownload> table = new Table<>(NodeDownload.class);
private static final Statement insert = table.insertFields("NodeId", "Link");
@Field(keys = {Table.PRIMARY})
private final int nodeId;
@Field
private final String link;
@Field(def = "CURRENT_TIMESTAMP")
private final Timestamp timestamp;
public static String getLink(SchematicNode schem){
if(schem.isDir())
throw new SecurityException("Can not Download Directorys");
MessageDigest digest;
try {
digest = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
throw new SecurityException(e);
}
digest.reset();
digest.update((Instant.now().toString() + schem.getOwner() + schem.getId()).getBytes());
String hash = base16encode(digest.digest());
insert.update(schem.getId(), hash);
return LINK_BASE + hash;
}
public static String base16encode(byte[] byteArray) {
StringBuilder hexBuffer = new StringBuilder(byteArray.length * 2);
for (byte b : byteArray)
hexBuffer.append(HEX[(b >>> 4) & 0xF]).append(HEX[b & 0xF]);
return hexBuffer.toString();
}
}

Datei anzeigen

@ -0,0 +1,91 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
@AllArgsConstructor
public class NodeMember {
public static void init() {
// enforce class initialization
}
private static final Table<NodeMember> table = new Table<>(NodeMember.class);
private static final SelectStatement<NodeMember> getNodeMember = table.select(Table.PRIMARY);
private static final SelectStatement<NodeMember> getNodeMembers = table.selectFields("NodeId");
private static final SelectStatement<NodeMember> getSchematics = table.selectFields("UserId");
private static final Statement create = table.insert(Table.PRIMARY);
private static final Statement delete = table.delete(Table.PRIMARY);
private static final Statement updateParent = table.update(Table.PRIMARY, "ParentId");
@Field(keys = {Table.PRIMARY})
private final int nodeId;
@Field(keys = {Table.PRIMARY})
private final int userId;
@Field(nullable = true, def = "null")
private Integer parentId;
public int getNode() {
return nodeId;
}
public int getMember() {
return userId;
}
public Optional<Integer> getParent() {
return Optional.ofNullable(parentId);
}
public void delete() {
delete.update(nodeId, userId);
}
public static NodeMember createNodeMember(int node, int member) {
create.update(node, member);
return new NodeMember(node, member, null);
}
public static NodeMember getNodeMember(int node, int member) {
return getNodeMember.select(node, member);
}
public static Set<NodeMember> getNodeMembers(int node) {
return new HashSet<>(getNodeMembers.listSelect(node));
}
public static Set<NodeMember> getSchematics(int member) {
return new HashSet<>(getSchematics.listSelect(member));
}
public void setParentId(Integer parentId) {
this.parentId = parentId;
updateParent.update(this.parentId, nodeId, userId);
}
}

Datei anzeigen

@ -0,0 +1,77 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import java.util.HashMap;
import java.util.Map;
@AllArgsConstructor
public class PollAnswer {
@Getter
@Setter
private static String currentPoll;
private static final Table<PollAnswer> table = new Table<>(PollAnswer.class);
private static final SelectStatement<PollAnswer> get = table.select(Table.PRIMARY);
private static final Statement getResults = new Statement("SELECT Count(UserID) AS Times, Answer FROM PollAnswer WHERE Question = ? GROUP BY Answer ORDER BY Times ASC");
private static final Statement insert = table.insertAll();
@Field(keys = {Table.PRIMARY})
private final int userID;
@Field(keys = {Table.PRIMARY})
private final String question;
@Field(def = "0")
private int answer;
public static PollAnswer get(int userID) {
PollAnswer answer = get.select(userID, currentPoll);
if(answer == null)
return new PollAnswer(userID, currentPoll, 0);
return answer;
}
public static Map<Integer, Integer> getCurrentResults() {
return getResults.select(rs -> {
Map<Integer, Integer> retMap = new HashMap<>();
while (rs.next())
retMap.put(rs.getInt("Answer")-1, rs.getInt("Times"));
return retMap;
}, currentPoll);
}
public boolean hasAnswered(){
return answer != 0;
}
public void setAnswer(int answer){
this.answer = answer;
insert.update(userID, question, answer);
}
}

Datei anzeigen

@ -0,0 +1,140 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@AllArgsConstructor
public class Punishment {
static {
SqlTypeMapper.nameEnumMapper(PunishmentType.class);
}
public static final Timestamp PERMA_TIME = Timestamp.from(Instant.ofEpochSecond(946674800));
private static final Table<Punishment> table = new Table<>(Punishment.class, "Punishments");
private static final SelectStatement<Punishment> getPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE PunishmentId IN (SELECT MAX(PunishmentId) FROM Punishments WHERE UserId = ? GROUP BY Type)");
private static final SelectStatement<Punishment> getPunishment = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? AND Type = ? ORDER BY PunishmentId DESC LIMIT 1");
private static final SelectStatement<Punishment> getAllPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? ORDER BY `PunishmentId` DESC");
private static final Statement insert = table.insertFields(true, "UserId", "Punisher", "Type", "EndTime", "Perma", "Reason");
public static Punishment getPunishmentOfPlayer(int user, PunishmentType type) {
return getPunishment.select(user, type);
}
public static Map<PunishmentType, Punishment> getPunishmentsOfPlayer(int user) {
return getPunishments.listSelect(user).stream().collect(Collectors.toMap(Punishment::getType, punishment -> punishment));
}
public static List<Punishment> getAllPunishmentsOfPlayer(int user) {
return getAllPunishments.listSelect(user);
}
public static boolean isPunished(SteamwarUser user, Punishment.PunishmentType type, Consumer<Punishment> callback) {
Punishment punishment = Punishment.getPunishmentOfPlayer(user.getId(), type);
if(punishment == null || !punishment.isCurrent()) {
return false;
} else {
callback.accept(punishment);
return true;
}
}
public static Punishment createPunishment(int user, int executor, PunishmentType type, String reason, Timestamp endTime, boolean perma) {
if(perma && !endTime.equals(PERMA_TIME)) {
throw new IllegalArgumentException("Permanent punishments must have an end time of `Punishment.PERMA_TIME`");
}
int punishmentId = insert.insertGetKey(user, executor, type.name(), endTime, perma, reason);
return new Punishment(punishmentId, user, executor, type, Timestamp.from(Instant.now()), endTime, perma, reason);
}
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int punishmentId;
@Field
@Getter
private final int userId;
@Field
@Getter
private final int punisher;
@Field
@Getter
private final PunishmentType type;
@Field
@Getter
private final Timestamp startTime;
@Field
@Getter
private final Timestamp endTime;
@Field
@Getter
private final boolean perma;
@Field
@Getter
private final String reason;
@Deprecated // Not multiling, misleading title
public String getBantime(Timestamp endTime, boolean perma) {
if (perma) {
return "permanent";
} else {
return endTime.toLocalDateTime().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"));
}
}
public boolean isCurrent() {
return isPerma() || getEndTime().after(new Date());
}
@AllArgsConstructor
@RequiredArgsConstructor
@Getter
public enum PunishmentType {
Ban(false, "BAN_TEAM", "BAN_PERMA", "BAN_UNTIL", "UNBAN_ERROR", "UNBAN"),
Mute( false, "MUTE_TEAM", "MUTE_PERMA", "MUTE_UNTIL", "UNMUTE_ERROR", "UNMUTE"),
NoSchemReceiving(false, "NOSCHEMRECEIVING_TEAM", "NOSCHEMRECEIVING_PERMA", "NOSCHEMRECEIVING_UNTIL", "UNNOSCHEMRECEIVING_ERROR", "UNNOSCHEMRECEIVING"),
NoSchemSharing(false, "NOSCHEMSHARING_TEAM", "NOSCHEMSHARING_PERMA", "NOSCHEMSHARING_UNTIL", "UNNOSCHEMSHARING_ERROR", "UNNOSCHEMSHARING"),
NoSchemSubmitting(true, "NOSCHEMSUBMITTING_TEAM", "NOSCHEMSUBMITTING_PERMA", "NOSCHEMSUBMITTING_UNTIL", "UNNOSCHEMSUBMITTING_ERROR", "UNNOSCHEMSUBMITTING"),
NoDevServer(true, "NODEVSERVER_TEAM", "NODEVSERVER_PERMA", "NODEVSERVER_UNTIL", "UNNODEVSERVER_ERROR", "UNNODEVSERVER"),
NoFightServer(false, "NOFIGHTSERVER_TEAM", "NOFIGHTSERVER_PERMA", "NOFIGHTSERVER_UNTIL", "UNNOFIGHTSERVER_ERROR", "UNNOFIGHTSERVER"),
NoTeamServer(true, "NOTEAMSERVER_TEAM", "NOTEAMSERVER_PERMA", "NOTEAMSERVER_UNTIL", "UNNOTEAMSERVER_ERROR", "UNNOTEAMSERVER"),
Note(false, "NOTE_TEAM", null, null, null, null, true);
private final boolean needsAdmin;
private final String teamMessage;
private final String playerMessagePerma;
private final String playerMessageUntil;
private final String usageNotPunished;
private final String unpunishmentMessage;
private boolean multi = false;
}
}

Datei anzeigen

@ -0,0 +1,44 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2024 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.util.Set;
import java.util.stream.Collectors;
@AllArgsConstructor
public class Referee {
private static final Table<Referee> table = new Table<>(Referee.class);
private static final SelectStatement<Referee> byEvent = table.selectFields("eventID");
public static Set<Integer> get(int eventID) {
return byEvent.listSelect(eventID).stream().map(referee -> referee.userID).collect(Collectors.toSet());
}
@Field(keys = {"eventReferee"})
private final int eventID;
@Field(keys = {"eventReferee"})
private final int userID;
}

Datei anzeigen

@ -0,0 +1,74 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.sql.SQLException;
@AllArgsConstructor
public class Replay {
static {
new SqlTypeMapper<>(File.class, "BLOB", (rs, identifier) -> {
try {
File file = File.createTempFile("replay", ".replay");
file.deleteOnExit();
Files.copy(rs.getBinaryStream(identifier), file.toPath(), StandardCopyOption.REPLACE_EXISTING);
return file;
} catch (IOException e) {
throw new SQLException(e);
}
}, (st, index, value) -> {
try {
st.setBinaryStream(index, new FileInputStream(value));
} catch (FileNotFoundException e) {
throw new SQLException(e);
}
});
}
private static final Table<Replay> table = new Table<>(Replay.class);
private static final SelectStatement<Replay> get = table.select(Table.PRIMARY);
public static final Statement insert = table.insertAll();
public static Replay get(int fightID) {
return get.select(fightID);
}
public static void save(int fightID, File file) {
insert.update(fightID, file);
}
@Field(keys = {Table.PRIMARY})
private final int fightID;
@Getter
@Field
private final File replay;
}

Datei anzeigen

@ -0,0 +1,33 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.ImplementationProvider;
import java.util.List;
import java.util.Map;
public interface SQLWrapper {
SQLWrapper impl = ImplementationProvider.getImpl("de.steamwar.sql.SQLWrapperImpl");
void loadSchemTypes(List<SchematicType> tmpTypes, Map<String, SchematicType> tmpFromDB);
void additionalExceptionMetadata(StringBuilder builder);
}

Datei anzeigen

@ -0,0 +1,68 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.io.File;
import java.sql.Timestamp;
@AllArgsConstructor
public class SWException {
public static void init() {
// force class initialialisation
}
private static final String CWD = System.getProperty("user.dir");
private static final String SERVER_NAME = new File(CWD).getName();
private static final Table<SWException> table = new Table<>(SWException.class, "Exception");
private static final Statement insert = table.insertFields(true, "server", "message", "stacktrace");
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int id;
@Field(def = "CURRENT_TIMESTAMP")
private final Timestamp time;
@Field
private final String server;
@Field
private final String message;
@Field
private final String stacktrace;
public static void log(String message, String stacktrace){
insert.update(SERVER_NAME, generateMessage(message), stacktrace);
}
public static int logGetId(String message, String stacktrace) {
return insert.insertGetKey(SERVER_NAME, generateMessage(message), stacktrace);
}
private static String generateMessage(String message) {
StringBuilder msgBuilder = new StringBuilder(message);
SQLWrapper.impl.additionalExceptionMetadata(msgBuilder);
msgBuilder.append("\nCWD: ").append(CWD);
return msgBuilder.toString();
}
}

Datei anzeigen

@ -0,0 +1,56 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
@AllArgsConstructor
public class SchemElo {
private static final int ELO_DEFAULT = 1000;
private static final Table<SchemElo> table = new Table<>(SchemElo.class);
private static final SelectStatement<SchemElo> select = table.select(Table.PRIMARY);
private static final Statement setElo = table.insertAll();
public static int getElo(SchematicNode node, int season) {
SchemElo elo = select.select(node, season);
return elo != null ? elo.elo : 0;
}
public static int getCurrentElo(int schemID) {
SchemElo elo = select.select(schemID, Season.getSeason());
return elo != null ? elo.elo : ELO_DEFAULT;
}
public static void setElo(int schemID, int elo) {
setElo.update(schemID, elo, Season.getSeason());
}
@Field(keys = {Table.PRIMARY})
private final int schemId;
@Field
private final int elo;
@Field(keys = {Table.PRIMARY})
private final int season;
}

Datei anzeigen

@ -0,0 +1,559 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.*;
import lombok.Getter;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.*;
import java.util.function.Predicate;
public class SchematicNode {
static {
SchematicType.Normal.name(); // Ensure SchematicType is loaded.
new SqlTypeMapper<>(SchematicNode.class, null, (rs, identifier) -> { throw new SecurityException("SchematicNode cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.nodeId));
}
private static final Map<Integer, Map<String, List<String>>> TAB_CACHE = new HashMap<>();
public static void clear() {
TAB_CACHE.clear();
}
private static final String nodeSelector = "SELECT NodeId, NodeOwner, NodeOwner AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode ";
private static final Table<SchematicNode> table = new Table<>(SchematicNode.class);
private static final Statement create = table.insertFields(true, "NodeOwner", "NodeName", "ParentNode", "NodeItem", "NodeType");
private static final Statement update = table.update(Table.PRIMARY, "NodeName", "ParentNode", "NodeItem", "NodeType", "NodeRank", "ReplaceColor", "AllowReplay");
private static final Statement delete = table.delete(Table.PRIMARY);
private static final SelectStatement<SchematicNode> byId = new SelectStatement<>(table, nodeSelector + "WHERE NodeId = ?");
private static final SelectStatement<SchematicNode> byOwnerNameParent = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeName = ? AND ParentNode " + Statement.NULL_SAFE_EQUALS + "?");
private static final SelectStatement<SchematicNode> byParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> dirsByParent = new SelectStatement<>(table, nodeSelector + "WHERE ParentNode" + Statement.NULL_SAFE_EQUALS + "? AND NodeType is NULL ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byOwnerType = new SelectStatement<>(table, nodeSelector + "WHERE NodeOwner = ? AND NodeType = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byType = new SelectStatement<>(table, nodeSelector + "WHERE NodeType = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> all = new SelectStatement<>(table, "SELECT * FROM EffectiveSchematicNode WHERE EffectiveOwner = ? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> list = new SelectStatement<>(table, "SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId " + Statement.NULL_SAFE_EQUALS + "? AND NM.UserId = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE (? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?) ORDER BY NodeName");
private static final SelectStatement<SchematicNode> byParentName = new SelectStatement<>(table, "SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, NM.ParentId AS ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode INNER JOIN NodeMember NM on SchematicNode.NodeId = NM.NodeId WHERE NM.ParentId " + Statement.NULL_SAFE_EQUALS + "? AND NM.UserId = ? AND SchematicNode.NodeName = ? UNION ALL SELECT SchematicNode.NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE ((? IS NULL AND ParentNode IS NULL AND NodeOwner = ?) OR (? IS NOT NULL AND ParentNode = ?)) AND NodeName = ?");
private static final SelectStatement<SchematicNode> schematicAccessibleForUser = new SelectStatement<>(table, "SELECT COUNT(DISTINCT NodeId) FROM EffectiveSchematicNode WHERE EffectiveOwner = ? AND NodeId = ?");
private static final SelectStatement<SchematicNode> accessibleByUserTypeInParent = new SelectStatement<>(table, "WITH RECURSIVE RSASN AS(WITH RECURSIVE RSAN AS (WITH RSANH AS (WITH RECURSIVE RSA AS (SELECT SN.NodeId, NM.ParentId FROM SchematicNode SN LEFT JOIN NodeMember NM on SN.NodeId = NM.NodeId WHERE NM.UserId = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN INNER JOIN RSA ON RSA.NodeId = SN.ParentNode) SELECT * FROM RSA UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?) SELECT * FROM RSANH UNION SELECT SN.NodeId, SN.ParentNode FROM RSANH JOIN SchematicNode SN ON SN.ParentNode = RSANH.NodeId) SELECT RSAN.NodeId, RSAN.ParentId FROM RSAN JOIN SchematicNode SN ON SN.NodeId = RSAN.NodeId WHERE NodeType = ? UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN JOIN RSASN ON SN.NodeId = RSASN.ParentId) SELECT SN.*, ? as EffectiveOwner, RSASN.ParentId AS ParentNode FROM RSASN JOIN SchematicNode SN ON SN.NodeId = RSASN.NodeId WHERE RSASN.ParentId" + Statement.NULL_SAFE_EQUALS + "? ORDER BY NodeName");
private static final SelectStatement<SchematicNode> accessibleByUserType = new SelectStatement<>(table, "WITH RECURSIVE Nodes AS (SELECT NodeId, ParentId as ParentNode FROM NodeMember WHERE UserId = ? UNION SELECT NodeId, ParentNode FROM SchematicNode WHERE NodeOwner = ?), RSN AS ( SELECT NodeId, ParentNode FROM Nodes UNION SELECT SN.NodeId, SN.ParentNode FROM SchematicNode SN, RSN WHERE SN.ParentNode = RSN.NodeId ) SELECT SN.*, ? AS EffectiveOwner FROM RSN INNER JOIN SchematicNode SN ON RSN.NodeId = SN.NodeId WHERE NodeType = ?");
private static final SelectStatement<SchematicNode> byIdAndUser = new SelectStatement<>(table, "SELECT NodeId, NodeOwner, ? AS EffectiveOwner, NodeName, ParentNode, LastUpdate, NodeItem, NodeType, NodeRank, ReplaceColor, AllowReplay FROM SchematicNode WHERE NodeId = ?");
private static final SelectStatement<SchematicNode> allParentsOfNode = new SelectStatement<>(table, "WITH RECURSIVE R AS (SELECT NodeId, ParentNode FROM EffectiveSchematicNode WHERE NodeId = ? AND EffectiveOwner = ? UNION SELECT E.NodeId, E.ParentNode FROM R, EffectiveSchematicNode E WHERE R.ParentNode = E.NodeId AND E.EffectiveOwner = ?) SELECT SN.NodeId, SN.NodeOwner, ? AS EffectiveOwner, SN.NodeName, R.ParentNode, SN.LastUpdate, SN.NodeItem, SN.NodeType, SN.NodeRank, SN.ReplaceColor, SN.AllowReplay FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId");
static {
NodeMember.init();
}
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int nodeId;
@Field(keys = {"OwnerNameParent"})
private final int nodeOwner;
@Field(def = "0")
@Getter
private final int effectiveOwner;
@Field(keys = {"OwnerNameParent"})
private String nodeName;
@Field(keys = {"OwnerNameParent"}, nullable = true)
private Integer parentNode;
@Field(def = "CURRENT_TIMESTAMP")
private Timestamp lastUpdate;
@Field(def = "''")
private String nodeItem;
@Field(def = "'normal'", nullable = true)
private SchematicType nodeType;
@Field(def = "0")
private int nodeRank;
@Field(def = "1")
private boolean replaceColor;
@Field(def = "1")
private boolean allowReplay;
private String brCache;
public SchematicNode(
int nodeId,
int nodeOwner,
int effectiveOwner,
String nodeName,
Integer parentNode,
Timestamp lastUpdate,
String nodeItem,
SchematicType nodeType,
int nodeRank,
boolean replaceColor,
boolean allowReplay
) {
this.nodeId = nodeId;
this.nodeOwner = nodeOwner;
this.effectiveOwner = effectiveOwner;
this.nodeName = nodeName;
this.parentNode = parentNode;
this.nodeItem = nodeItem;
this.nodeType = nodeType;
this.lastUpdate = lastUpdate;
this.nodeRank = nodeRank;
this.replaceColor = replaceColor;
this.allowReplay = allowReplay;
}
public static List<SchematicNode> getAll(SteamwarUser user) {
return all.listSelect(user);
}
public static Map<Integer, List<SchematicNode>> getAllMap(SteamwarUser user) {
return map(getAll(user));
}
public static List<SchematicNode> list(SteamwarUser user, Integer schematicId) {
return list.listSelect(user, schematicId, user, user, schematicId, user, schematicId, schematicId);
}
public static SchematicNode byParentName(SteamwarUser user, Integer schematicId, String name) {
return byParentName.select(user, schematicId, user, name, user, schematicId, user, schematicId, schematicId, name);
}
public static List<SchematicNode> accessibleByUserType(SteamwarUser user, SchematicType type) {
return accessibleByUserType.listSelect(user, user, user, type);
}
public static Map<Integer, List<SchematicNode>> accessibleByUserTypeMap(SteamwarUser user, SchematicType type) {
return map(accessibleByUserType(user, type));
}
public static boolean schematicAccessibleForUser(SteamwarUser user, Integer schematicId) {
return schematicAccessibleForUser.select(user, schematicId) != null;
}
public static List<SchematicNode> accessibleByUserTypeParent(SteamwarUser user, SchematicType type, Integer parentId) {
return accessibleByUserTypeInParent.listSelect(user, user, type, user, parentId);
}
public static SchematicNode byIdAndUser(SteamwarUser user, Integer id) {
return byIdAndUser.select(user, id);
}
public static List<SchematicNode> parentsOfNode(SteamwarUser user, Integer id) {
return allParentsOfNode.listSelect(id, user, user, user);
}
private static Map<Integer, List<SchematicNode>> map(List<SchematicNode> in) {
Map<Integer, List<SchematicNode>> map = new HashMap<>();
for (SchematicNode effectiveSchematicNode : in) {
map.computeIfAbsent(effectiveSchematicNode.getOptionalParent().orElse(0), k -> new ArrayList<>()).add(effectiveSchematicNode);
}
return map;
}
public static SchematicNode createSchematic(int owner, String name, Integer parent) {
return createSchematicNode(owner, name, parent, SchematicType.Normal.toDB(), "");
}
public static SchematicNode createSchematicDirectory(int owner, String name, Integer parent) {
return createSchematicNode(owner, name, parent, null, "");
}
public static SchematicNode createSchematicNode(int owner, String name, Integer parent, String type, String item) {
if (parent != null && parent == 0)
parent = null;
int nodeId = create.insertGetKey(owner, name, parent, item, type);
return getSchematicNode(nodeId);
}
public static SchematicNode getSchematicNode(int owner, String name, SchematicNode parent) {
return getSchematicNode(owner, name, parent.getId());
}
public static SchematicNode getSchematicNode(int owner, String name, Integer parent) {
return byOwnerNameParent.select(owner, name, parent);
}
public static List<SchematicNode> getSchematicNodeInNode(SchematicNode parent) {
return getSchematicNodeInNode(parent.getId());
}
public static List<SchematicNode> getSchematicNodeInNode(Integer parent) {
return byParent.listSelect(parent);
}
public static List<SchematicNode> getSchematicDirectoryInNode(Integer parent) {
return dirsByParent.listSelect(parent);
}
@Deprecated
public static SchematicNode getSchematicDirectory(String name, SchematicNode parent) {
return getSchematicNode(name, parent.getId());
}
@Deprecated
public static SchematicNode getSchematicDirectory(String name, Integer parent) {
return getSchematicNode(name, parent);
}
public static SchematicNode getSchematicNode(String name, Integer parent) {
return byParentName.select(name, parent);
}
public static SchematicNode getSchematicNode(int id) {
return byId.select(id);
}
public static List<SchematicNode> getAccessibleSchematicsOfTypeInParent(int owner, String schemType, Integer parent) {
return accessibleByUserTypeParent(SteamwarUser.get(owner), SchematicType.fromDB(schemType), parent);
}
public static List<SchematicNode> getAllAccessibleSchematicsOfType(int user, String schemType) {
return accessibleByUserType(SteamwarUser.get(user), SchematicType.fromDB(schemType));
}
public static List<SchematicNode> getAllSchematicsOfType(int owner, String schemType) {
return byOwnerType.listSelect(owner, schemType);
}
@Deprecated
public static List<SchematicNode> getAllSchematicsOfType(String schemType) {
return byType.listSelect(schemType);
}
public static List<SchematicNode> getAllSchematicsOfType(SchematicType schemType) {
return byType.listSelect(schemType);
}
public static List<SchematicNode> deepGet(Integer parent, Predicate<SchematicNode> filter) {
List<SchematicNode> finalList = new ArrayList<>();
List<SchematicNode> nodes = SchematicNode.getSchematicNodeInNode(parent);
nodes.forEach(node -> {
if (node.isDir()) {
finalList.addAll(deepGet(node.getId(), filter));
} else {
if (filter.test(node))
finalList.add(node);
}
});
return finalList;
}
@Deprecated
public static List<SchematicNode> getSchematicsAccessibleByUser(int user, Integer parent) {
return list(SteamwarUser.get(user), parent);
}
@Deprecated
public static List<SchematicNode> getAllSchematicsAccessibleByUser(int user) {
return getAll(SteamwarUser.get(user));
}
public static List<SchematicNode> getAllParentsOfNode(SchematicNode node) {
return getAllParentsOfNode(node.getId());
}
public static List<SchematicNode> getAllParentsOfNode(int node) {
return allParentsOfNode.listSelect(node);
}
public static SchematicNode getNodeFromPath(SteamwarUser user, String s) {
if (s.startsWith("/")) {
s = s.substring(1);
}
if (s.endsWith("/")) {
s = s.substring(0, s.length() - 1);
}
if (s.isEmpty()) {
return null;
}
if (s.contains("/")) {
String[] layers = s.split("/");
Optional<SchematicNode> currentNode = Optional.ofNullable(SchematicNode.byParentName(user, null, layers[0]));
for (int i = 1; i < layers.length; i++) {
int finalI = i;
Optional<SchematicNode> node = currentNode.map(effectiveSchematicNode -> SchematicNode.byParentName(user, effectiveSchematicNode.getId(), layers[finalI]));
if (!node.isPresent()) {
return null;
} else {
currentNode = node;
if (!currentNode.map(SchematicNode::isDir).orElse(false) && i != layers.length - 1) {
return null;
}
}
}
return currentNode.orElse(null);
} else {
return SchematicNode.byParentName(user, null, s);
}
}
public static List<SchematicNode> filterSchems(int user, Predicate<SchematicNode> filter) {
List<SchematicNode> finalList = new ArrayList<>();
List<SchematicNode> nodes = getSchematicsAccessibleByUser(user, null);
nodes.forEach(node -> {
if (node.isDir()) {
finalList.addAll(deepGet(node.getId(), filter));
} else {
if (filter.test(node))
finalList.add(node);
}
});
return finalList;
}
public int getId() {
return nodeId;
}
public int getOwner() {
return nodeOwner;
}
public String getName() {
return nodeName;
}
public void setName(String name) {
this.nodeName = name;
updateDB();
}
public Integer getParent() {
return parentNode;
}
public Optional<Integer> getOptionalParent() {
return Optional.ofNullable(parentNode);
}
public void setParent(Integer parent) {
this.parentNode = parent;
updateDB();
}
public String getItem() {
if (nodeItem.isEmpty()) {
return isDir() ? "CHEST" : "CAULDRON_ITEM";
}
return nodeItem;
}
public void setItem(String item) {
this.nodeItem = item;
updateDB();
}
@Deprecated
public String getType() {
return nodeType.name();
}
@Deprecated
public void setType(String type) {
if(isDir())
throw new SecurityException("Node is Directory");
this.nodeType = SchematicType.fromDB(type);
updateDB();
}
public boolean isDir() {
return nodeType == null;
}
@Deprecated
public boolean getSchemFormat() {
if(isDir())
throw new SecurityException("Node is Directory");
return NodeData.get(this).getNodeFormat();
}
public int getRank() {
if(isDir())
throw new SecurityException("Node is Directory");
return nodeRank;
}
@Deprecated
public int getRankUnsafe() {
return nodeRank;
}
public void setRank(int rank) {
if(isDir())
throw new SecurityException("Node is Directory");
this.nodeRank = rank;
}
public SchematicType getSchemtype() {
if(isDir())
throw new SecurityException("Is Directory");
return nodeType;
}
public void setSchemtype(SchematicType type) {
if(isDir())
throw new SecurityException("Is Directory");
this.nodeType = type;
updateDB();
}
public boolean replaceColor() {
return replaceColor;
}
public void setReplaceColor(boolean replaceColor) {
if(isDir())
throw new SecurityException("Is Directory");
this.replaceColor = replaceColor;
updateDB();
}
public boolean allowReplay() {
return allowReplay;
}
public void setAllowReplay(boolean allowReplay) {
if(isDir())
throw new SecurityException("Is Directory");
this.allowReplay = allowReplay;
updateDB();
}
public SchematicNode getParentNode() {
if(parentNode == null) return null;
return SchematicNode.getSchematicNode(parentNode);
}
public int getElo(int season) {
return SchemElo.getElo(this, season);
}
public boolean accessibleByUser(int user) {
return NodeMember.getNodeMember(nodeId, user) != null;
}
public Set<NodeMember> getMembers() {
return NodeMember.getNodeMembers(nodeId);
}
public Timestamp getLastUpdate() {
return lastUpdate;
}
private void updateDB() {
this.lastUpdate = Timestamp.from(Instant.now());
update.update(nodeName, parentNode, nodeItem, nodeType, nodeRank, replaceColor, allowReplay, nodeId);
TAB_CACHE.clear();
}
public void delete() {
delete.update(nodeId);
}
@Override
public int hashCode() {
return nodeId;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof SchematicNode))
return false;
return ((SchematicNode) obj).getId() == nodeId;
}
public String generateBreadcrumbs(SteamwarUser user) {
return byIdAndUser(user, nodeId).generateBreadcrumbs();
}
public String generateBreadcrumbs(String split, SteamwarUser user) {
return byIdAndUser(user, nodeId).generateBreadcrumbs(split);
}
public String generateBreadcrumbs() {
if(brCache == null) {
brCache = generateBreadcrumbs("/");
}
return brCache;
}
public String generateBreadcrumbs(String split) {
StringBuilder builder = new StringBuilder(getName());
Optional<SchematicNode> currentNode = Optional.of(this);
if(currentNode.map(SchematicNode::isDir).orElse(false)) {
builder.append(split);
}
while (currentNode.isPresent()) {
currentNode = currentNode.flatMap(schematicNode -> Optional.ofNullable(NodeMember.getNodeMember(schematicNode.getId(), effectiveOwner)).map(NodeMember::getParent).orElse(schematicNode.getOptionalParent())).map(SchematicNode::getSchematicNode);
currentNode.ifPresent(node -> builder.insert(0, split).insert(0, node.getName()));
}
return builder.toString();
}
private static final List<String> FORBIDDEN_NAMES = Collections.unmodifiableList(Arrays.asList("public"));
public static boolean invalidSchemName(String[] layers) {
for (String layer : layers) {
if (layer.isEmpty()) {
return true;
}
if (layer.contains("/") ||
layer.contains("\\") ||
layer.contains("<") ||
layer.contains(">") ||
layer.contains("^") ||
layer.contains("°") ||
layer.contains("'") ||
layer.contains("\"") ||
layer.contains(" ")) {
return true;
}
if(FORBIDDEN_NAMES.contains(layer.toLowerCase())) {
return true;
}
}
return false;
}
public static List<String> getNodeTabcomplete(SteamwarUser user, String s) {
boolean sws = s.startsWith("/");
if (sws) {
s = s.substring(1);
}
int index = s.lastIndexOf("/");
String cacheKey = index == -1 ? "" : s.substring(0, index);
if(TAB_CACHE.containsKey(user.getId()) && TAB_CACHE.get(user.getId()).containsKey(cacheKey)) {
return new ArrayList<>(TAB_CACHE.get(user.getId()).get(cacheKey));
}
List<String> list = new ArrayList<>();
if (s.contains("/")) {
String preTab = s.substring(0, s.lastIndexOf("/") + 1);
SchematicNode pa = SchematicNode.getNodeFromPath(user, preTab);
if (pa == null) return new ArrayList<>();
List<SchematicNode> nodes = SchematicNode.list(user, pa.getId());
String br = pa.generateBreadcrumbs();
nodes.forEach(node -> list.add((sws ? "/" : "") + br + node.getName() + (node.isDir() ? "/" : "")));
} else {
List<SchematicNode> nodes = SchematicNode.list(user, null);
nodes.forEach(node -> list.add((sws ? "/" : "") + node.getName() + (node.isDir() ? "/" : "")));
}
list.remove("//copy");
TAB_CACHE.computeIfAbsent(user.getId(), integer -> new HashMap<>()).putIfAbsent(cacheKey, list);
return list;
}
}

Datei anzeigen

@ -0,0 +1,124 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.SqlTypeMapper;
import lombok.Getter;
import java.util.*;
public class SchematicType {
public static final SchematicType Normal = new SchematicType("Normal", "", Type.NORMAL, null, "STONE_BUTTON", false);
private static final Map<String, SchematicType> fromDB;
private static final List<SchematicType> types;
static {
List<SchematicType> tmpTypes = new LinkedList<>();
Map<String, SchematicType> tmpFromDB = new HashMap<>();
tmpTypes.add(Normal);
tmpFromDB.put(Normal.name().toLowerCase(), Normal);
SQLWrapper.impl.loadSchemTypes(tmpTypes, tmpFromDB);
fromDB = Collections.unmodifiableMap(tmpFromDB);
types = Collections.unmodifiableList(tmpTypes);
}
static {
new SqlTypeMapper<>(SchematicType.class, "VARCHAR(16)", (rs, identifier) -> {
String t = rs.getString(identifier);
return t != null ? fromDB.get(t) : null;
}, (st, index, value) -> st.setString(index, value.toDB()));
}
private final String name;
@Getter
private final String kuerzel;
private final Type type;
private final SchematicType checkType;
@Getter
private final String material;
@Getter
private final Date deadline;
@Getter
private final boolean manualCheck;
SchematicType(String name, String kuerzel, Type type, SchematicType checkType, String material, boolean manualCheck){
this(name, kuerzel, type, checkType, material, null, manualCheck);
}
SchematicType(String name, String kuerzel, Type type, SchematicType checkType, String material, Date deadline, boolean manualCheck){
this.name = name;
this.kuerzel = kuerzel;
this.type = type;
this.checkType = checkType;
this.material = material;
this.deadline = deadline;
this.manualCheck = manualCheck;
}
public boolean isAssignable(){
return type == Type.NORMAL || (type == Type.FIGHT_TYPE && checkType != null) || !manualCheck;
}
public SchematicType checkType(){
if (!manualCheck) {
return this;
}
return checkType;
}
public boolean check(){
return type == Type.CHECK_TYPE;
}
public boolean fightType(){
return type == Type.FIGHT_TYPE;
}
public boolean writeable(){
return type == Type.NORMAL;
}
public String name(){
return name;
}
public String toDB(){
return name.toLowerCase();
}
public static SchematicType fromDB(String input){
return fromDB.get(input.toLowerCase());
}
public static List<SchematicType> values(){
return types;
}
enum Type{
NORMAL,
CHECK_TYPE,
FIGHT_TYPE
}
}

Datei anzeigen

@ -0,0 +1,84 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
@AllArgsConstructor
@Getter
public class Script {
private static final Table<Script> table = new Table<>(Script.class);
private static final SelectStatement<Script> byId = table.select(Table.PRIMARY);
private static final SelectStatement<Script> byUserName = table.select("nameUser");
private static final SelectStatement<Script> byUser = table.selectFields("userId");
private static final Statement insert = table.insertFields(true, "userId", "name", "code");
private static final Statement updateName = table.update(Table.PRIMARY, "name");
private static final Statement updateCode = table.update(Table.PRIMARY, "code");
private static final Statement delete = table.delete(Table.PRIMARY);
public static Script get(int id) {
return byId.select(id);
}
public static Script get(SteamwarUser user, String name) {
return byUserName.select(user, name);
}
public static Script create(SteamwarUser user, String name, String code) {
return new Script(insert.insertGetKey(user, name, code), user.getId(), name, code);
}
public static List<Script> list(SteamwarUser user) {
return byUser.listSelect(user);
}
@Field(keys = Table.PRIMARY, autoincrement = true)
private final int id;
@Field(keys = "nameUser")
private final int userId;
@Field(keys = "nameUser")
private String name;
@Field
private String code;
public void setName(String name) {
this.name = name;
updateName.update(name, id);
}
public void setCode(String code) {
this.code = code;
updateCode.update(code, id);
}
public void delete() {
delete.update(id);
}
}

Datei anzeigen

@ -0,0 +1,61 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import java.util.Calendar;
public class Season {
private Season() {}
public static int getSeason() {
Calendar calendar = Calendar.getInstance();
int yearIndex = calendar.get(Calendar.MONTH) / 4;
return (calendar.get(Calendar.YEAR) * 3 + yearIndex);
}
public static String getSeasonStart() {
Calendar calendar = Calendar.getInstance();
int month = calendar.get(Calendar.MONTH);
if (month <= 3) {
return calendar.get(Calendar.YEAR) + "-1-1";
} else if (month <= 7) {
return calendar.get(Calendar.YEAR) + "-5-1";
} else {
return calendar.get(Calendar.YEAR) + "-9-1";
}
}
public static String convertSeasonToString(int season){
if (season == -1) return "";
int yearSeason = season % 3;
int year = (season - yearSeason) / 3;
return String.format("%d-%d", year, yearSeason);
}
public static int convertSeasonToNumber(String season){
if (season.isEmpty()) return -1;
String[] split = season.split("-");
try {
return Integer.parseInt(split[0]) * 3 + Integer.parseInt(split[1]);
} catch (NumberFormatException e) {
return -1;
}
}
}

Datei anzeigen

@ -0,0 +1,45 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.sql.Timestamp;
@AllArgsConstructor
public class Session {
private static final Table<Session> table = new Table<>(Session.class);
private static final Statement insert = table.insert(Table.PRIMARY);
public static void insertSession(int userID, Timestamp startTime){
insert.update(userID, startTime);
}
@Field(keys = {Table.PRIMARY})
private int userId;
@Field(keys = {Table.PRIMARY})
private Timestamp startTime;
@Field(def = "CURRENT_TIMESTAMP")
private Timestamp endTime;
}

Datei anzeigen

@ -0,0 +1,362 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.*;
import lombok.Getter;
import lombok.SneakyThrows;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.sql.Timestamp;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.stream.Collectors;
public class SteamwarUser {
private static final SecureRandom random = new SecureRandom();
private static final SecretKeyFactory factory;
static {
try {
factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
} catch (NoSuchAlgorithmException e) {
throw new SecurityException(e);
}
new SqlTypeMapper<>(UUID.class, "CHAR(36)", (rs, identifier) -> UUID.fromString(rs.getString(identifier)), (st, index, value) -> st.setString(index, value.toString()));
new SqlTypeMapper<>(Locale.class, "VARCHAR(32)", (rs, identifier) -> {
String l = rs.getString(identifier);
return l != null ? Locale.forLanguageTag(l) : null;
}, (st, index, value) -> st.setString(index, value.toLanguageTag()));
new SqlTypeMapper<>(SteamwarUser.class, null, (rs, identifier) -> { throw new SecurityException("SteamwarUser cannot be used as type (recursive select)"); }, (st, index, value) -> st.setInt(index, value.id));
}
private static final Table<SteamwarUser> table = new Table<>(SteamwarUser.class, "UserData");
private static final Statement insert = table.insertFields("UUID", "UserName");
private static final SelectStatement<SteamwarUser> byID = table.selectFields("id");
private static final SelectStatement<SteamwarUser> byUUID = table.selectFields("UUID");
private static final SelectStatement<SteamwarUser> byName = table.selectFields("UserName");
private static final SelectStatement<SteamwarUser> byDiscord = table.selectFields("DiscordId");
private static final SelectStatement<SteamwarUser> byTeam = table.selectFields("Team");
private static final SelectStatement<SteamwarUser> getServerTeam = new SelectStatement<>(table, "SELECT * FROM UserData WHERE UserGroup != 'Member' AND UserGroup != 'YouTuber'");
private static final Statement updateName = table.update(Table.PRIMARY, "UserName");
private static final Statement updatePassword = table.update(Table.PRIMARY, "Password");
private static final Statement updateLocale = table.update(Table.PRIMARY, "Locale", "ManualLocale");
private static final Statement updateTeam = table.update(Table.PRIMARY, "Team");
private static final Statement updateLeader = table.update(Table.PRIMARY, "Leader");
private static final Statement updateDiscord = table.update(Table.PRIMARY, "DiscordId");
private static final Statement getPlaytime = new Statement("SELECT SUM(UNIX_TIMESTAMP(EndTime) - UNIX_TIMESTAMP(StartTime)) as Playtime FROM Session WHERE UserID = ?");
private static final Statement getFirstjoin = new Statement("SELECT MIN(StartTime) AS FirstJoin FROM Session WHERE UserID = ?");
private static final Map<Integer, SteamwarUser> usersById = new HashMap<>();
private static final Map<UUID, SteamwarUser> usersByUUID = new HashMap<>();
private static final Map<String, SteamwarUser> usersByName = new HashMap<>();
private static final Map<Long, SteamwarUser> usersByDiscord = new HashMap<>();
public static void clear() {
usersById.clear();
usersByName.clear();
usersByUUID.clear();
usersByDiscord.clear();
}
public static void invalidate(int userId) {
SteamwarUser user = usersById.remove(userId);
if (user == null)
return;
usersByName.remove(user.getUserName());
usersByUUID.remove(user.getUUID());
usersByDiscord.remove(user.getDiscordId());
}
public static SteamwarUser get(String userName){
SteamwarUser user = usersByName.get(userName.toLowerCase());
if(user != null)
return user;
return byName.select(userName);
}
public static SteamwarUser get(UUID uuid){
SteamwarUser user = usersByUUID.get(uuid);
if(user != null)
return user;
return byUUID.select(uuid);
}
public static SteamwarUser get(int id) {
SteamwarUser user = usersById.get(id);
if(user != null)
return user;
return byID.select(id);
}
public static SteamwarUser get(Long discordId) {
if(usersByDiscord.containsKey(discordId))
return usersByDiscord.get(discordId);
return byDiscord.select(discordId);
}
public static SteamwarUser getOrCreate(UUID uuid, String name, Consumer<UUID> newPlayer, BiConsumer<String, String> nameUpdate) {
SteamwarUser user = get(uuid);
if (user != null) {
if (!user.userName.equals(name)) {
updateName.update(name, user.id);
nameUpdate.accept(user.userName, name);
user.userName = name;
}
return user;
} else {
insert.update(uuid, name);
newPlayer.accept(uuid);
return get(uuid);
}
}
public static List<SteamwarUser> getServerTeam() {
return getServerTeam.listSelect();
}
public static List<SteamwarUser> getTeam(int teamId) {
return byTeam.listSelect(teamId);
}
public static void batchCache(Set<Integer> ids) {
ids.removeIf(usersById::containsKey);
if(ids.isEmpty())
return;
try (SelectStatement<SteamwarUser> batch = new SelectStatement<>(table, "SELECT * FROM UserData WHERE id IN (" + ids.stream().map(Object::toString).collect(Collectors.joining(", ")) + ")")) {
batch.listSelect();
}
}
@Getter
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int id;
@Field(keys = {"uuid"})
private final UUID uuid;
@Getter
@Field
private String userName;
@Field(nullable = true)
private String password;
@Getter
@Field(def = "0")
private int team;
@Getter
@Field(def = "0")
private boolean leader;
@Field(nullable = true)
private Locale locale;
@Field(def = "0")
private boolean manualLocale;
@Getter
@Field(keys = {"discordId"}, nullable = true)
private Long discordId;
private Map<Punishment.PunishmentType, Punishment> punishments = null;
private Set<UserPerm> permissions = null;
private UserPerm.Prefix prefix = null;
public SteamwarUser(int id, UUID uuid, String userName, String password, int team, boolean leader, Locale locale, boolean manualLocale, Long discordId) {
this.id = id;
this.uuid = uuid;
this.userName = userName;
this.password = password;
this.team = team;
this.leader = leader;
this.locale = locale;
this.manualLocale = manualLocale;
this.discordId = discordId != null && discordId != 0 ? discordId : null;
usersById.put(id, this);
usersByName.put(userName.toLowerCase(), this);
usersByUUID.put(uuid, this);
if (this.discordId != null) {
usersByDiscord.put(discordId, this);
}
}
public UUID getUUID() {
return uuid;
}
public Locale getLocale() {
if(locale != null)
return locale;
return Locale.getDefault();
}
public Punishment getPunishment(Punishment.PunishmentType type) {
initPunishments();
return punishments.getOrDefault(type, null);
}
public boolean isPunished(Punishment.PunishmentType punishment) {
initPunishments();
if (!punishments.containsKey(punishment)) {
return false;
}
if (!punishments.get(punishment).isCurrent()) {
if (punishment == Punishment.PunishmentType.Ban) {
BannedUserIPs.unbanIPs(id);
}
punishments.remove(punishment);
return false;
}
return true;
}
public boolean hasPerm(UserPerm perm) {
initPerms();
return permissions.contains(perm);
}
public Set<UserPerm> perms() {
initPerms();
return permissions;
}
public UserPerm.Prefix prefix() {
initPerms();
return prefix;
}
public double getOnlinetime() {
return getPlaytime.select(rs -> {
if (rs.next() && rs.getBigDecimal("Playtime") != null)
return rs.getBigDecimal("Playtime").doubleValue();
return 0.0;
}, id);
}
public Timestamp getFirstjoin() {
return getFirstjoin.select(rs -> {
if (rs.next())
return rs.getTimestamp("FirstJoin");
return null;
}, id);
}
public void punish(Punishment.PunishmentType punishment, Timestamp time, String banReason, int from, boolean perma) {
initPunishments();
punishments.remove(punishment);
punishments.put(punishment, Punishment.createPunishment(id, from, punishment, banReason, time, perma));
}
public void setTeam(int team) {
this.team = team;
updateTeam.update(team, id);
setLeader(false);
}
public void setLeader(boolean leader) {
this.leader = leader;
updateLeader.update(leader, id);
}
public void setLocale(Locale locale, boolean manualLocale) {
if (locale == null || (this.manualLocale && !manualLocale))
return;
this.locale = locale;
this.manualLocale = manualLocale;
updateLocale.update(locale.toLanguageTag(), manualLocale, id);
}
public void setDiscordId(Long discordId) {
usersByDiscord.remove(this.discordId);
this.discordId = discordId;
updateDiscord.update(discordId, id);
if (discordId != null) {
usersByDiscord.put(discordId, this);
}
}
public void setPassword(String password) {
try {
byte[] salt = new byte[16];
random.nextBytes(salt);
String saltString = Base64.getEncoder().encodeToString(salt);
byte[] hash = generateHash(password, salt);
String hashString = Base64.getEncoder().encodeToString(hash);
this.password = hashString + ":" + saltString;
updatePassword.update(this.password, id);
} catch (Exception e) {
throw new SecurityException(e);
}
}
public boolean verifyPassword(String password) {
try {
if (this.password == null) {
return false;
}
String[] parts = this.password.split(":");
if (parts.length != 2) {
SQLConfig.impl.getLogger().log(Level.SEVERE ,"Invalid password hash for user {0} ({1})", new Object[]{userName, id});
return false;
}
String hashString = parts[0];
byte[] realHash = Base64.getDecoder().decode(hashString);
String saltString = parts[1];
byte[] salt = Base64.getDecoder().decode(saltString);
byte[] hash = generateHash(password, salt);
return Arrays.equals(realHash, hash);
} catch (Exception e) {
SQLConfig.impl.getLogger().log(Level.SEVERE, "Error while verifying password for user " + userName + " (" + id + ")", e);
return false;
}
}
private byte[] generateHash(String password, byte[] salt)
throws InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 65536, 512);
return factory.generateSecret(spec).getEncoded();
}
private void initPunishments() {
if(punishments != null)
return;
punishments = Punishment.getPunishmentsOfPlayer(id);
}
private void initPerms() {
if(permissions != null)
return;
permissions = UserPerm.getPerms(id);
prefix = permissions.stream().filter(UserPerm.prefixes::containsKey).findAny().map(UserPerm.prefixes::get).orElse(UserPerm.emptyPrefix);
}
}

140
src/de/steamwar/sql/Team.java Normale Datei
Datei anzeigen

@ -0,0 +1,140 @@
/*
This file is a part of the SteamWar software.
Copyright (C) 2020 SteamWar.de-Serverteam
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@AllArgsConstructor
public class Team {
private static final Map<Integer, Team> teamCache = new HashMap<>();
public static void clear() {
teamCache.clear();
}
private static final Table<Team> table = new Table<>(Team.class);
private static final SelectStatement<Team> byId = table.select(Table.PRIMARY);
private static final SelectStatement<Team> byName = new SelectStatement<>(table, "SELECT * FROM Team WHERE (lower(TeamName) = ? OR lower(TeamKuerzel) = ?) AND NOT TeamDeleted");
private static final SelectStatement<Team> all = table.selectFields("TeamDeleted");
private static final Statement insert = table.insertFields("TeamKuerzel", "TeamName");
private static final Statement update = table.update(Table.PRIMARY, "TeamKuerzel", "TeamName", "TeamColor", "Address", "Port");
private static final Statement delete = table.update(Table.PRIMARY, "TeamDeleted");
private static final Statement getSize = new Statement("SELECT COUNT(id) FROM UserData WHERE Team = ?");
@Field(keys = {Table.PRIMARY}, autoincrement = true)
@Getter
private final int teamId;
@Field
@Getter
private String teamKuerzel;
@Field
@Getter
private String teamName;
@Field(def = "'8'")
@Getter
private String teamColor;
@Field(nullable = true)
@Getter
private String address;
@Field(def = "'25565'")
@Getter
private int port;
@Field(def = "0")
private boolean teamDeleted;
public static void create(String kuerzel, String name){
insert.update(kuerzel, name);
}
public static Team get(int id) {
return teamCache.computeIfAbsent(id, byId::select);
}
public static Team get(String name){
// No cache lookup due to low frequency use
name = name.toLowerCase();
return byName.select(name, name);
}
public static List<Team> getAll(){
clear();
List<Team> teams = all.listSelect(false);
teams.forEach(team -> teamCache.put(team.getTeamId(), team));
return teams;
}
public List<Integer> getMembers(){
return SteamwarUser.getTeam(teamId).stream().map(SteamwarUser::getId).collect(Collectors.toList());
}
public int size(){
return getSize.select(rs -> {
rs.next();
return rs.getInt("COUNT(id)");
}, teamId);
}
public void disband(SteamwarUser user){
user.setLeader(false);
delete.update(true, teamId);
teamCache.remove(teamId);
TeamTeilnahme.deleteFuture(teamId);
}
public void setTeamKuerzel(String teamKuerzel) {
this.teamKuerzel = teamKuerzel;
updateDB();
}
public void setTeamName(String teamName) {
this.teamName = teamName;
updateDB();
}
public void setTeamColor(String teamColor) {
this.teamColor = teamColor;
updateDB();
}
public void setAddress(String address) {
this.address = address;
updateDB();
}
public void setPort(int port) {
this.port = port;
updateDB();
}
private void updateDB(){
update.update(teamKuerzel, teamName, teamColor, address, port, teamId);
}
}

Datei anzeigen

@ -0,0 +1,70 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.util.Set;
import java.util.stream.Collectors;
@AllArgsConstructor
public class TeamTeilnahme {
private static final Table<TeamTeilnahme> table = new Table<>(TeamTeilnahme.class);
private static final SelectStatement<TeamTeilnahme> select = table.select(Table.PRIMARY);
private static final SelectStatement<TeamTeilnahme> selectTeams = table.selectFields("EventID");
private static final SelectStatement<TeamTeilnahme> selectEvents = table.selectFields("TeamID");
private static final Statement insert = table.insert(Table.PRIMARY);
private static final Statement delete = table.delete(Table.PRIMARY);
private static final Statement deleteFuture = new Statement("DELETE t FROM TeamTeilnahme t INNER JOIN Event e ON t.EventID = e.EventID WHERE t.TeamID = ? AND e.Start > NOW()");
@Field(keys = {Table.PRIMARY})
private final int teamId;
@Field(keys = {Table.PRIMARY})
private final int eventId;
public static boolean nimmtTeil(int teamID, int eventID){
return select.select(teamID, eventID) != null;
}
public static void teilnehmen(int teamID, int eventID){
insert.update(teamID, eventID);
}
public static void notTeilnehmen(int teamID, int eventID){
delete.update(teamID, eventID);
}
public static void deleteFuture(int teamID) {
deleteFuture.update(teamID);
}
public static Set<Team> getTeams(int eventID){
return selectTeams.listSelect(eventID).stream().map(tt -> Team.get(tt.teamId)).collect(Collectors.toSet()); // suboptimal performance (O(n) database queries)
}
public static Set<Event> getEvents(int teamID){
return selectEvents.listSelect(teamID).stream().map(tt -> Event.get(tt.eventId)).collect(Collectors.toSet()); // suboptimal performance (O(n) database queries)
}
}

110
src/de/steamwar/sql/Token.java Normale Datei
Datei anzeigen

@ -0,0 +1,110 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.ToString;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.sql.Timestamp;
import java.util.Base64;
import java.util.List;
@AllArgsConstructor
@Getter
@ToString
public class Token {
private static final Table<Token> table = new Table<>(Token.class);
private static final Statement insert = table.insertFields(true, "Name", "Owner", "Hash");
private static final SelectStatement<Token> get = table.select(Table.PRIMARY);
private static final SelectStatement<Token> listUser = table.selectFields("owner");
private static final SelectStatement<Token> getHash = table.selectFields("hash");
private static final Statement delete = table.delete(Table.PRIMARY);
@SneakyThrows
private static String getHash(String code) {
return Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-512").digest(code.getBytes()));
}
@SneakyThrows
public static String createToken(String name, SteamwarUser owner) {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[20];
random.nextBytes(bytes);
String code = Base64.getEncoder().encodeToString(bytes);
String hash = getHash(code);
create(name, owner, hash);
return code;
}
public static Token getTokenByCode(String code) {
String hash = getHash(code);
return get(hash);
}
public static Token create(String name, SteamwarUser owner, String hash) {
int id = insert.insertGetKey(name, owner, hash);
return get(id);
}
public static Token get(int id) {
return get.select(id);
}
public static List<Token> listUser(SteamwarUser owner) {
return listUser.listSelect(owner);
}
public static Token get(String hash) {
return getHash.select(hash);
}
public static void delete(Token id) {
delete.update(id.getId());
}
@Field(keys = Table.PRIMARY, autoincrement = true)
private final int id;
@Field(keys = "NameOwner")
private final String name;
@Field(keys = "NameOwner")
private final int owner;
@Field
private final Timestamp created;
@Field
private final String hash;
public void delete() {
delete(this);
}
public SteamwarUser getOwner() {
return SteamwarUser.get(owner);
}
}

Datei anzeigen

@ -0,0 +1,94 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
import java.util.stream.Collectors;
@AllArgsConstructor
public class Tutorial {
private static final Table<Tutorial> table = new Table<>(Tutorial.class);
private static final SelectStatement<Tutorial> by_popularity = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Released = ? GROUP BY t.TutorialID ORDER BY SUM(r.Stars) DESC LIMIT ?, ?");
private static final SelectStatement<Tutorial> own = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Creator = ? GROUP BY t.TutorialID ORDER BY t.TutorialID ASC LIMIT ?, ?");
private static final SelectStatement<Tutorial> by_creator_name = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.Creator = ? AND t.Name = ? GROUP BY t.TutorialID");
private static final SelectStatement<Tutorial> by_id = new SelectStatement<>(table, "SELECT t.*, AVG(r.Stars) AS Stars FROM Tutorial t LEFT OUTER JOIN TutorialRating r ON t.TutorialID = r.TutorialID WHERE t.TutorialID = ? GROUP BY t.TutorialID");
private static final Statement rate = new Statement("INSERT INTO TutorialRating (TutorialID, UserID, Stars) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE Stars = VALUES(Stars)");
private static final Statement create = new Statement("INSERT INTO Tutorial (Creator, Name, Item) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE Item = VALUES(Item), Released = 0");
private static final Statement release = table.update(Table.PRIMARY, "released");
private static final Statement delete = table.delete(Table.PRIMARY);
public static List<Tutorial> getPage(int page, int elementsPerPage, boolean released) {
List<Tutorial> tutorials = by_popularity.listSelect(released, page * elementsPerPage, elementsPerPage);
SteamwarUser.batchCache(tutorials.stream().map(tutorial -> tutorial.creator).collect(Collectors.toSet()));
return tutorials;
}
public static List<Tutorial> getOwn(int user, int page, int elementsPerPage) {
return own.listSelect(user, page * elementsPerPage, elementsPerPage);
}
public static Tutorial create(int creator, String name, String item) {
create.update(creator, name, item);
return by_creator_name.select(creator, name);
}
public static Tutorial get(int id) {
return by_id.select(id);
}
@Getter
@Field(keys = {Table.PRIMARY}, autoincrement = true)
private final int tutorialId;
@Getter
@Field(keys = {"CreatorName"})
private final int creator;
@Getter
@Field(keys = {"CreatorName"})
private final String name;
@Getter
@Field(def = "'BOOK'")
private final String item;
@Getter
@Field(def = "0")
private final boolean released;
@Getter
@Field(def = "0") // Not really a field, but necessary for select generation
private final double stars;
public void release() {
release.update(1, tutorialId);
}
public void delete() {
delete.update(tutorialId);
}
public void rate(int user, int rating) {
rate.update(tutorialId, user, rating);
}
}

Datei anzeigen

@ -0,0 +1,73 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.util.UUID;
@AllArgsConstructor
public class UserConfig {
private static final Table<UserConfig> table = new Table<>(UserConfig.class);
private static final SelectStatement<UserConfig> select = table.select(Table.PRIMARY);
private static final Statement insert = table.insertAll();
private static final Statement delete = table.delete(Table.PRIMARY);
@Field(keys = {Table.PRIMARY})
private final int user;
@Field(keys = {Table.PRIMARY})
private final String config;
@Field
private final String value;
public static String getConfig(UUID player, String config) {
return getConfig(SteamwarUser.get(player).getId(), config);
}
public static String getConfig(int player, String config) {
UserConfig value = select.select(player, config);
return value != null ? value.value : null;
}
public static void updatePlayerConfig(UUID uuid, String config, String value) {
updatePlayerConfig(SteamwarUser.get(uuid).getId(), config, value);
}
public static void updatePlayerConfig(int id, String config, String value) {
if (value == null) {
removePlayerConfig(id, config);
return;
}
insert.update(id, config, value);
}
public static void removePlayerConfig(UUID uuid, String config) {
removePlayerConfig(SteamwarUser.get(uuid).getId(), config);
}
public static void removePlayerConfig(int id, String config) {
delete.update(id, config);
}
}

Datei anzeigen

@ -0,0 +1,188 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.Statement;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@AllArgsConstructor
public class UserElo {
private static final int ELO_DEFAULT = 0;
private static final Map<String, Map<Integer, Optional<Integer>>> gameModeUserEloCache = new ConcurrentHashMap<>();
private static final Map<Integer, String> emblemCache = new ConcurrentHashMap<>();
public static void clear() {
gameModeUserEloCache.clear();
emblemCache.clear();
}
private static final Table<UserElo> table = new Table<>(UserElo.class);
private static final SelectStatement<UserElo> getElo = table.select(Table.PRIMARY);
private static final Statement setElo = table.insertAll();
private static final Statement place = new Statement("SELECT COUNT(*) AS Place FROM UserElo WHERE GameMode = ? AND Elo > ? AND Season = ?");
private static final Statement fightsOfSeason = new Statement("SELECT COUNT(*) AS Fights FROM FightPlayer INNER JOIN Fight F on FightPlayer.FightID = F.FightID WHERE UserID = ? AND GameMode = ? AND UNIX_TIMESTAMP(StartTime) + Duration >= UNIX_TIMESTAMP(?)");
@Field(keys = {Table.PRIMARY})
private final int season;
@Field(keys = {Table.PRIMARY})
private final String gameMode;
@Field(keys = {Table.PRIMARY})
private final int userId;
@Field
private final int elo;
public static int getEloOrDefault(int userID, String gameMode) {
return getElo(userID, gameMode).orElse(ELO_DEFAULT);
}
public static Optional<Integer> getElo(int userID, String gameMode) {
return gameModeUserEloCache.computeIfAbsent(gameMode, gm -> new HashMap<>()).computeIfAbsent(userID, uid -> Optional.ofNullable(getElo.select(Season.getSeason(), gameMode, userID)).map(userElo -> userElo.elo));
}
public static int getFightsOfSeason(int userID, String gameMode) {
return fightsOfSeason.select(rs -> {
if (rs.next())
return rs.getInt("Fights");
return 0;
}, userID, gameMode, Season.getSeasonStart());
}
public static void setElo(int userId, String gameMode, int elo) {
emblemCache.remove(userId);
int oldPlacement = getPlacement(getElo(userId, gameMode).orElse(0), gameMode);
int newPlacement = getPlacement(elo, gameMode);
gameModeUserEloCache.getOrDefault(gameMode, Collections.emptyMap()).remove(userId);
if (oldPlacement <= 3 || newPlacement <= 3) {
emblemCache.clear();
}
setElo.update(Season.getSeason(), gameMode, userId, elo);
}
public static int getPlacement(int elo, String gameMode) {
return place.select(rs -> {
if (rs.next())
return rs.getInt("Place") + 1;
return -1;
}, gameMode, elo, Season.getSeason());
}
public static String getEmblem(SteamwarUser user, List<String> rankedModes) {
return emblemCache.computeIfAbsent(user.getId(), userId -> {
int emblemProgression = -1;
for (String mode : rankedModes) {
if (UserElo.getFightsOfSeason(userId, mode) == 0) continue;
int progression = getProgression(userId, mode);
if (progression > emblemProgression) {
emblemProgression = progression;
}
}
return toEmblem(emblemProgression);
});
}
public static String getEmblemProgression(String gameMode, int userId) {
switch (getProgression(userId, gameMode)) {
case -1:
return "§f/ §8 ∧ ❂ III II I";
case 0:
return "§8/ §6 §8∧ ∧ ❂ III II I";
case 1:
return "§8/ §6∧ §8 ∧ ❂ III II I";
case 2:
return "§8/ ∧ §7 §8∧ ∧ ❂ III II I";
case 3:
return "§8/ §7∧ §8 ∧ ❂ III II I";
case 4:
return "§8/ ∧ §e §8∧ ❂ III II I";
case 5:
return "§8/ §e∧ §8❂ III II I";
case 6:
return "§8/ ∧ §5❂ §8III II I";
case 7:
return "§8/ ∧ ❂ §5III §8II I";
case 8:
return "§8/ ∧ ❂ III §5II §8I";
case 9:
return "§8/ ∧ ❂ III II §5I";
default:
throw new SecurityException("Progression is not in range");
}
}
public static int getProgression(int userId, String gameMode) {
int elo = getElo(userId, gameMode).orElse(-1);
if (elo < 0) return -1;
if (elo <= 100) return 0;
if (elo <= 200) return 1;
if (elo <= 400) return 2;
if (elo <= 600) return 3;
if (elo <= 900) return 4;
if (elo <= 1200) return 5;
int placement = getPlacement(elo, gameMode);
if (placement == 1) return 9;
if (placement == 2) return 8;
if (placement == 3) return 7;
return 6;
}
public static String toEmblem(int progression) {
switch(progression) {
case -1:
return "";
case 0:
return "§6 ";
case 1:
return "§6∧ ";
case 2:
return "§7 ";
case 3:
return "§7∧ ";
case 4:
return "§e ";
case 5:
return "§e∧ ";
case 6:
return "§5❂ ";
case 7:
return "§5III ";
case 8:
return "§5II ";
case 9:
return "§5I ";
default:
throw new SecurityException("Progression out of range");
}
}
}

Datei anzeigen

@ -0,0 +1,90 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2023 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql;
import de.steamwar.sql.internal.Field;
import de.steamwar.sql.internal.SelectStatement;
import de.steamwar.sql.internal.SqlTypeMapper;
import de.steamwar.sql.internal.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.*;
import java.util.stream.Collectors;
public enum UserPerm {
PREFIX_NONE, // special value, not stored in database
PREFIX_YOUTUBER,
PREFIX_GUIDE,
PREFIX_BUILDER,
PREFIX_SUPPORTER,
PREFIX_MODERATOR,
PREFIX_DEVELOPER,
PREFIX_ADMIN,
RESTRICTED_MODS,
COLOR_CHAT,
TEAM,
TICKET_LOG,
BUILD,
CHECK,
MODERATION,
ADMINISTRATION;
public static final Map<UserPerm, Prefix> prefixes;
public static final Prefix emptyPrefix;
static {
SqlTypeMapper.nameEnumMapper(UserPerm.class);
Map<UserPerm, Prefix> p = new EnumMap<>(UserPerm.class);
emptyPrefix = new Prefix("§7", "");
p.put(PREFIX_NONE, emptyPrefix);
p.put(PREFIX_YOUTUBER, new Prefix("§7", "YT"));
p.put(PREFIX_GUIDE, new Prefix("§a", "Guide"));
p.put(PREFIX_SUPPORTER, new Prefix("§6", "Sup"));
p.put(PREFIX_MODERATOR, new Prefix("§6", "Mod"));
p.put(PREFIX_BUILDER, new Prefix("§e", "Arch"));
p.put(PREFIX_DEVELOPER, new Prefix("§e", "Dev"));
p.put(PREFIX_ADMIN, new Prefix("§e", "Admin"));
prefixes = Collections.unmodifiableMap(p);
}
private static final Table<UserPermTable> table = new Table<>(UserPermTable.class, "UserPerm");
private static final SelectStatement<UserPermTable> getPerms = table.selectFields("user");
public static Set<UserPerm> getPerms(int user) {
return getPerms.listSelect(user).stream().map(up -> up.perm).collect(Collectors.toSet());
}
@Getter
@AllArgsConstructor
public static class Prefix {
private final String colorCode;
private final String chatPrefix;
}
@AllArgsConstructor
public static class UserPermTable {
@Field(keys = {Table.PRIMARY})
private final int user;
@Field(keys = {Table.PRIMARY})
private final UserPerm perm;
}
}

Datei anzeigen

@ -0,0 +1,34 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql.internal;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Field {
String[] keys() default {};
String def() default "";
boolean nullable() default false;
boolean autoincrement() default false;
}

Datei anzeigen

@ -0,0 +1,34 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql.internal;
import de.steamwar.ImplementationProvider;
import java.util.logging.Logger;
public interface SQLConfig {
SQLConfig impl = ImplementationProvider.getImpl("de.steamwar.sql.SQLConfigImpl");
Logger getLogger();
int maxConnections();
}

Datei anzeigen

@ -0,0 +1,72 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql.internal;
import java.lang.reflect.InvocationTargetException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class SelectStatement<T> extends Statement {
private final Table<T> table;
SelectStatement(Table<T> table, String... kfields) {
this(table, "SELECT " + Arrays.stream(table.fields).map(f -> f.identifier).collect(Collectors.joining(", ")) + " FROM " + table.name + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND ")));
}
public SelectStatement(Table<T> table, String sql) {
super(sql);
this.table = table;
}
public T select(Object... values) {
return select(rs -> {
if (rs.next())
return read(rs);
return null;
}, values);
}
public List<T> listSelect(Object... values) {
return select(rs -> {
List<T> result = new ArrayList<>();
while (rs.next())
result.add(read(rs));
return result;
}, values);
}
private T read(ResultSet rs) throws SQLException {
Object[] params = new Object[table.fields.length];
for(int i = 0; i < params.length; i++) {
params[i] = table.fields[i].read(rs);
}
try {
return table.constructor.newInstance(params);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new SecurityException(e);
}
}
}

Datei anzeigen

@ -0,0 +1,115 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql.internal;
import java.io.InputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.Map;
public final class SqlTypeMapper<T> {
private static final Map<Class<?>, SqlTypeMapper<?>> mappers = new IdentityHashMap<>();
public static <T> SqlTypeMapper<T> getMapper(Class<?> clazz) {
SqlTypeMapper<T> result = (SqlTypeMapper<T>) mappers.get(clazz);
if(result == null)
throw new SecurityException("Unregistered mapper requested: " + clazz.getName());
return result;
}
public static <T extends Enum<T>> void ordinalEnumMapper(Class<T> type) {
T[] enumConstants = type.getEnumConstants();
new SqlTypeMapper<>(
type,
"INTEGER(" + (int)Math.ceil(enumConstants.length/256.0) + ")",
(rs, identifier) -> enumConstants[rs.getInt(identifier)],
(st, index, value) -> st.setInt(index, value.ordinal())
);
}
public static <T extends Enum<T>> void nameEnumMapper(Class<T> type) {
new SqlTypeMapper<>(
type,
"VARCHAR(" + Arrays.stream(type.getEnumConstants()).map(e -> e.name().length()).max(Integer::compareTo).orElse(0) + ")",
(rs, identifier) -> Enum.valueOf(type, rs.getString(identifier)),
(st, index, value) -> st.setString(index, value.name())
);
}
static {
primitiveMapper(boolean.class, Boolean.class, "BOOLEAN", ResultSet::getBoolean, PreparedStatement::setBoolean);
primitiveMapper(byte.class, Byte.class, "INTEGER(1)", ResultSet::getByte, PreparedStatement::setByte);
primitiveMapper(short.class, Short.class, "INTEGER(2)", ResultSet::getShort, PreparedStatement::setShort);
primitiveMapper(int.class, Integer.class, "INTEGER", ResultSet::getInt, PreparedStatement::setInt);
primitiveMapper(long.class, Long.class, "INTEGER(8)", ResultSet::getLong, PreparedStatement::setLong);
primitiveMapper(float.class, Float.class, "REAL", ResultSet::getFloat, PreparedStatement::setFloat);
primitiveMapper(double.class, Double.class, "REAL", ResultSet::getDouble, PreparedStatement::setDouble);
new SqlTypeMapper<>(String.class, "TEXT", ResultSet::getString, PreparedStatement::setString);
new SqlTypeMapper<>(Timestamp.class, "TIMESTAMP", ResultSet::getTimestamp, PreparedStatement::setTimestamp);
new SqlTypeMapper<>(InputStream.class, "BLOB", ResultSet::getBinaryStream, PreparedStatement::setBinaryStream);
}
private static <T> void primitiveMapper(Class<T> primitive, Class<T> wrapped, String sqlType, SQLReader<T> reader, SQLWriter<T> writer) {
new SqlTypeMapper<>(primitive, sqlType, reader, writer);
new SqlTypeMapper<>(wrapped, sqlType, (rs, identifier) -> {
T value = reader.read(rs, identifier);
return rs.wasNull() ? null : value;
}, writer);
}
private final String sqlType;
private final SQLReader<T> reader;
private final SQLWriter<T> writer;
public SqlTypeMapper(Class<T> clazz, String sqlType, SQLReader<T> reader, SQLWriter<T> writer) {
this.sqlType = sqlType;
this.reader = reader;
this.writer = writer;
mappers.put(clazz, this);
}
public T read(ResultSet rs, String identifier) throws SQLException {
return reader.read(rs, identifier);
}
public void write(PreparedStatement st, int index, Object value) throws SQLException {
writer.write(st, index, (T) value);
}
public String sqlType() {
return sqlType;
}
@FunctionalInterface
public interface SQLReader<T> {
T read(ResultSet rs, String identifier) throws SQLException;
}
@FunctionalInterface
public interface SQLWriter<T> {
void write(PreparedStatement st, int index, T value) throws SQLException;
}
}

Datei anzeigen

@ -0,0 +1,298 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql.internal;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.sql.*;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Statement implements AutoCloseable {
private static final Logger logger = SQLConfig.impl.getLogger();
private static final List<Statement> statements = new ArrayList<>();
private static final Deque<Connection> connections = new ArrayDeque<>();
private static final int MAX_CONNECTIONS;
private static final Supplier<Connection> conProvider;
static final Consumer<Table<?>> schemaCreator;
static final String ON_DUPLICATE_KEY;
static final UnaryOperator<String> upsertWrapper;
public static final String NULL_SAFE_EQUALS;
private static final boolean MYSQL_MODE;
private static final boolean PRODUCTION_DATABASE;
static {
File file = new File(System.getProperty("user.home"), "mysql.properties");
MYSQL_MODE = file.exists();
if(MYSQL_MODE) {
Properties properties = new Properties();
try {
properties.load(new FileReader(file));
} catch (IOException e) {
throw new SecurityException("Could not load SQL connection", e);
}
String url = "jdbc:mysql://" + properties.getProperty("host") + ":" + properties.getProperty("port") + "/" + properties.getProperty("database") + "?useServerPrepStmts=true";
String user = properties.getProperty("user");
String password = properties.getProperty("password");
PRODUCTION_DATABASE = "core".equals(properties.getProperty("database"));
MAX_CONNECTIONS = SQLConfig.impl.maxConnections();
conProvider = () -> {
try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
throw new SecurityException("Could not create MySQL connection", e);
}
};
schemaCreator = table -> {};
ON_DUPLICATE_KEY = " ON DUPLICATE KEY UPDATE ";
upsertWrapper = f -> f + " = VALUES(" + f + ")";
NULL_SAFE_EQUALS = " <=> ";
} else {
Connection connection;
try {
Class.forName("org.sqlite.JDBC");
connection = DriverManager.getConnection("jdbc:sqlite:" + System.getProperty("user.home") + "/standalone.db");
} catch (SQLException | ClassNotFoundException e) {
throw new SecurityException("Could not create sqlite connection", e);
}
PRODUCTION_DATABASE = false;
MAX_CONNECTIONS = 1;
conProvider = () -> connection;
schemaCreator = Table::ensureExistanceInSqlite;
ON_DUPLICATE_KEY = " ON CONFLICT DO UPDATE SET ";
upsertWrapper = f -> f + " = " + f;
NULL_SAFE_EQUALS = " IS ";
}
}
private static volatile int connectionBudget = MAX_CONNECTIONS;
public static void closeAll() {
synchronized (connections) {
while(connectionBudget < MAX_CONNECTIONS) {
if(connections.isEmpty())
waitOnConnections();
else
closeConnection(aquireConnection());
}
}
}
public static boolean mysqlMode() {
return MYSQL_MODE;
}
public static boolean productionDatabase() {
return PRODUCTION_DATABASE;
}
private final boolean returnGeneratedKeys;
private final String sql;
private final Map<Connection, PreparedStatement> cachedStatements = new HashMap<>();
public Statement(String sql) {
this(sql, false);
}
public Statement(String sql, boolean returnGeneratedKeys) {
this.sql = sql;
this.returnGeneratedKeys = returnGeneratedKeys;
synchronized (statements) {
statements.add(this);
}
}
public <T> T select(ResultSetUser<T> user, Object... objects) {
return withConnection(st -> {
boolean res = st.execute();
if(!res) {
throw new SecurityException("No result set for select statement");
}
ResultSet rs = st.getResultSet();
T result = user.use(rs);
rs.close();
return result;
}, objects);
}
public void update(Object... objects) {
withConnection(PreparedStatement::executeUpdate, objects);
}
public int insertGetKey(Object... objects) {
return withConnection(st -> {
st.executeUpdate();
ResultSet rs = st.getGeneratedKeys();
rs.next();
return rs.getInt(1);
}, objects);
}
public String getSql() {
return sql;
}
private <T> T withConnection(SQLRunnable<T> runnable, Object... objects) {
Connection connection = aquireConnection();
T result;
try {
result = tryWithConnection(connection, runnable, objects);
} catch (Throwable e) {
if(connectionInvalid(connection)) {
closeConnection(connection);
return withConnection(runnable, objects);
} else {
synchronized (connections) {
connections.push(connection);
connections.notify();
}
throw new SecurityException("Failing sql statement", e);
}
}
synchronized (connections) {
connections.push(connection);
connections.notify();
}
return result;
}
private boolean connectionInvalid(Connection connection) {
try {
return connection.isClosed() || !connection.isValid(1);
} catch (SQLException e) {
logger.log(Level.INFO, "Could not check SQL connection status", e); // No database logging possible at this state
return true;
}
}
private <T> T tryWithConnection(Connection connection, SQLRunnable<T> runnable, Object... objects) throws SQLException {
PreparedStatement st = cachedStatements.get(connection);
if(st == null) {
if(returnGeneratedKeys)
st = connection.prepareStatement(sql, java.sql.Statement.RETURN_GENERATED_KEYS);
else
st = connection.prepareStatement(sql);
cachedStatements.put(connection, st);
}
for (int i = 0; i < objects.length; i++) {
Object o = objects[i];
if(o != null)
SqlTypeMapper.getMapper(o.getClass()).write(st, i+1, o);
else
st.setNull(i+1, Types.NULL);
}
return runnable.run(st);
}
@Override
public void close() {
cachedStatements.values().forEach(st -> closeStatement(st, false));
cachedStatements.clear();
synchronized (statements) {
statements.remove(this);
}
}
private void close(Connection connection) {
PreparedStatement st = cachedStatements.remove(connection);
if(st != null)
closeStatement(st, true);
}
private static Connection aquireConnection() {
synchronized (connections) {
while(connections.isEmpty() && connectionBudget <= 0)
waitOnConnections();
if(!connections.isEmpty()) {
return connections.pop();
} else {
Connection connection = conProvider.get();
connectionBudget--;
return connection;
}
}
}
private static void closeConnection(Connection connection) {
synchronized (statements) {
for (Statement statement : statements) {
statement.close(connection);
}
}
try {
connection.close();
} catch (SQLException e) {
logger.log(Level.INFO, "Could not close connection", e);
}
synchronized (connections) {
connectionBudget++;
connections.notify();
}
}
private static void waitOnConnections() {
synchronized (connections) {
try {
connections.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
private static void closeStatement(PreparedStatement st, boolean silent) {
try {
st.close();
} catch (SQLException e) {
if(!silent)
logger.log(Level.INFO, "Could not close statement", e);
}
}
public interface ResultSetUser<T> {
T use(ResultSet rs) throws SQLException;
}
private interface SQLRunnable<T> {
T run(PreparedStatement st) throws SQLException;
}
}

Datei anzeigen

@ -0,0 +1,137 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.sql.internal;
import java.lang.reflect.Constructor;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Table<T> {
public static final String PRIMARY = "primary";
final String name;
final TableField<?>[] fields;
private final Map<String, TableField<?>> fieldsByIdentifier = new HashMap<>();
final Constructor<T> constructor;
private final Map<String, Table.TableField<?>[]> keys;
public Table(Class<T> clazz) {
this(clazz, clazz.getSimpleName());
}
public Table(Class<T> clazz, String name) {
this.name = name;
this.fields = Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(Field.class)).map(TableField::new).toArray(TableField[]::new);
try {
this.constructor = clazz.getDeclaredConstructor(Arrays.stream(clazz.getDeclaredFields()).filter(field -> field.isAnnotationPresent(Field.class)).map(java.lang.reflect.Field::getType).toArray(Class[]::new));
} catch (NoSuchMethodException e) {
throw new SecurityException(e);
}
keys = Arrays.stream(fields).flatMap(field -> Arrays.stream(field.field.keys())).distinct().collect(Collectors.toMap(Function.identity(), key -> Arrays.stream(fields).filter(field -> Arrays.asList(field.field.keys()).contains(key)).toArray(TableField[]::new)));
for (TableField<?> field : fields) {
fieldsByIdentifier.put(field.identifier.toLowerCase(), field);
}
Statement.schemaCreator.accept(this);
}
public SelectStatement<T> select(String name) {
return selectFields(keyFields(name));
}
public SelectStatement<T> selectFields(String... kfields) {
return new SelectStatement<>(this, kfields);
}
public Statement update(String name, String... fields) {
return updateFields(fields, keyFields(name));
}
public Statement updateField(String field, String... kfields) {
return updateFields(new String[]{field}, kfields);
}
public Statement updateFields(String[] fields, String... kfields) {
return new Statement("UPDATE " + name + " SET " + Arrays.stream(fields).map(f -> f + " = ?").collect(Collectors.joining(", ")) + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND ")));
}
public Statement insert(String name) {
return insertFields(keyFields(name));
}
public Statement insertAll() {
return insertFields(false, Arrays.stream(fields).map(f -> f.identifier).toArray(String[]::new));
}
public Statement insertFields(String... fields) {
return insertFields(false, fields);
}
public Statement insertFields(boolean returnGeneratedKeys, String... fields) {
List<String> nonKeyFields = Arrays.stream(fields).filter(f -> fieldsByIdentifier.get(f.toLowerCase()).field.keys().length == 0).collect(Collectors.toList());
return new Statement("INSERT INTO " + name + " (" + String.join(", ", fields) + ") VALUES (" + Arrays.stream(fields).map(f -> "?").collect(Collectors.joining(", ")) + ")" + (nonKeyFields.isEmpty() ? "" : Statement.ON_DUPLICATE_KEY + nonKeyFields.stream().map(Statement.upsertWrapper).collect(Collectors.joining(", "))), returnGeneratedKeys);
}
public Statement delete(String name) {
return deleteFields(keyFields(name));
}
public Statement deleteFields(String... kfields) {
return new Statement("DELETE FROM " + name + " WHERE " + Arrays.stream(kfields).map(f -> f + " = ?").collect(Collectors.joining(" AND ")));
}
void ensureExistanceInSqlite() {
try (Statement statement = new Statement(
"CREATE TABLE IF NOT EXISTS " + name + "(" +
Arrays.stream(fields).map(field -> field.identifier + " " + field.mapper.sqlType() + (field.field.nullable() ? " DEFAULT NULL" : " NOT NULL") + (field.field.nullable() || field.field.def().equals("") ? "" : " DEFAULT " + field.field.def())).collect(Collectors.joining(", ")) +
keys.entrySet().stream().map(key -> (key.getKey().equals(PRIMARY) ? ", PRIMARY KEY(" : ", UNIQUE (") + Arrays.stream(key.getValue()).map(field -> field.identifier).collect(Collectors.joining(", ")) + ")").collect(Collectors.joining(" ")) +
")")) {
statement.update();
}
}
private String[] keyFields(String name) {
return Arrays.stream(keys.get(name)).map(f -> f.identifier).toArray(String[]::new);
}
static class TableField<T> {
final String identifier;
final SqlTypeMapper<T> mapper;
private final Field field;
private TableField(java.lang.reflect.Field field) {
this.identifier = field.getName();
this.mapper = SqlTypeMapper.getMapper(field.getType());
this.field = field.getAnnotation(Field.class);
}
T read(ResultSet rs) throws SQLException {
return mapper.read(rs, identifier);
}
}
}

Datei anzeigen

@ -1,5 +1,6 @@
build:
setup:
- "ln -s /home/gitea/lib"
- "cp ~/gradle.properties ."
- "chmod u+x build.gradle"
build:
- "./gradlew buildProject"
- "./gradlew --stop"

Datei anzeigen

@ -57,4 +57,18 @@ public class ArgumentCommand extends TestSWCommand {
public void argument(String sender, String arg) {
throw new ExecutionIdentifier("RunArgument with String");
}
@Register
public void minLengthArgument(String sender, @Length(min = 3) @StaticValue({"he", "hello"}) String arg) {
throw new ExecutionIdentifier("RunLengthArgument with String");
}
@Register
public void minAndMaxLengthArgument(String sender, @Length(min = 3, max = 3) @StaticValue({"wo", "world"}) String arg) {
throw new ExecutionIdentifier("RunLengthArgument with String");
}
public void arrayLengthArgument(String sender, @ArrayLength(max = 3) @StaticValue({"one", "two", "three"}) String... args) {
throw new ExecutionIdentifier("RunArrayLengthArgument with String");
}
}

Datei anzeigen

@ -44,6 +44,7 @@ public class ArgumentCommandTest {
ArgumentCommand cmd = new ArgumentCommand();
try {
cmd.execute("test", "", new String[]{"true", "false"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Boolean");
}
@ -54,6 +55,7 @@ public class ArgumentCommandTest {
ArgumentCommand cmd = new ArgumentCommand();
try {
cmd.execute("test", "", new String[]{"0.0", "0.0", "0.0"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Float");
}
@ -64,6 +66,7 @@ public class ArgumentCommandTest {
ArgumentCommand cmd = new ArgumentCommand();
try {
cmd.execute("test", "", new String[]{"0.0", "0.0", "0.0", "0.0"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Double");
}
@ -74,6 +77,7 @@ public class ArgumentCommandTest {
ArgumentCommand cmd = new ArgumentCommand();
try {
cmd.execute("test", "", new String[]{"0"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Integer");
}
@ -84,26 +88,17 @@ public class ArgumentCommandTest {
ArgumentCommand cmd = new ArgumentCommand();
try {
cmd.execute("test", "", new String[]{"0", "0"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with Long");
}
}
@Test
public void testString() {
ArgumentCommand cmd = new ArgumentCommand();
try {
cmd.execute("test", "", new String[]{"Hello World"});
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunArgument with String");
}
}
@Test
public void testTabComplete() {
ArgumentCommand cmd = new ArgumentCommand();
List<String> strings = cmd.tabComplete("test", "", new String[]{""});
assertTabCompletes(strings, "true", "false");
assertTabCompletes(strings, "true", "false", "hello", "wor");
}
@Test
@ -111,5 +106,23 @@ public class ArgumentCommandTest {
ArgumentCommand cmd = new ArgumentCommand();
List<String> strings = cmd.tabComplete("test", "", new String[]{"t"});
assertTabCompletes(strings, "true", "t");
strings = cmd.tabComplete("test", "", new String[]{"h"});
assertTabCompletes(strings, "h", "hello");
strings = cmd.tabComplete("test", "", new String[]{"hel"});
assertTabCompletes(strings, "hel", "hello");
strings = cmd.tabComplete("test", "", new String[]{"w"});
assertTabCompletes(strings, "w", "wor");
strings = cmd.tabComplete("test", "", new String[]{"wor"});
assertTabCompletes(strings, "wor");
strings = cmd.tabComplete("test", "", new String[]{"worl"});
assertTabCompletes(strings, "wor", "worl");
strings = cmd.tabComplete("test", "", new String[]{"one", "two", "three", "one"});
assertTabCompletes(strings);
}
}

Datei anzeigen

@ -0,0 +1,58 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import de.steamwar.command.dto.TestSWCommand;
import de.steamwar.command.dto.TestTypeMapper;
import java.util.Collection;
public class BetterExceptionCommand extends TestSWCommand {
public BetterExceptionCommand() {
super("betterexception");
}
@Register
public void exceptionOnTabComplete(String s, @Mapper("exception") @Validator("exception") String s1) {
}
@Mapper("exception")
@Validator("exception")
public TestTypeMapper<String> tabCompleteException() {
return new TestTypeMapper<String>() {
@Override
public String map(String sender, PreviousArguments previousArguments, String s) {
return null;
}
@Override
public boolean validate(String sender, String value, MessageSender messageSender) {
System.out.println("Validate: " + value);
throw new SecurityException();
}
@Override
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) {
throw new SecurityException();
}
};
}
}

Datei anzeigen

@ -0,0 +1,53 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
public class BetterExceptionCommandTest {
@Test
public void testTabCompleteException() {
BetterExceptionCommand cmd = new BetterExceptionCommand();
try {
cmd.tabComplete("test", "", new String[]{""});
assert false;
} catch (Exception e) {
assertThat(e, is(instanceOf(CommandFrameworkException.class)));
assertThat(e.getMessage(), is("de.steamwar.command.CommandFrameworkException: Error while tabcompleting () to type java.lang.String with parameter index 1"));
}
}
@Test
public void testValidationException() {
BetterExceptionCommand cmd = new BetterExceptionCommand();
try {
cmd.execute("test", "", new String[]{""});
assert false;
} catch (Exception e) {
assertThat(e, is(instanceOf(CommandFrameworkException.class)));
assertThat(e.getMessage(), is("de.steamwar.command.CommandFrameworkException: Error while validating () to type java.lang.String with parameter index 1"));
}
}
}

Datei anzeigen

@ -41,15 +41,14 @@ public class CacheCommand extends TestSWCommand {
@Cached
@Mapper(value = "int", local = true)
public AbstractTypeMapper<String, Integer> typeMapper() {
System.out.println("TypeMapper register");
return new TestTypeMapper<Integer>() {
@Override
public Integer map(String sender, String[] previousArguments, String s) {
public Integer map(String sender, PreviousArguments previousArguments, String s) {
return Integer.parseInt(s);
}
@Override
public Collection<String> tabCompletes(String sender, String[] previousArguments, String s) {
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) {
return Arrays.asList(count.getAndIncrement() + "");
}
};

Datei anzeigen

@ -36,6 +36,14 @@ public class CacheCommandTest {
assertThat(tabCompletions1, is(equalTo(tabCompletions2)));
}
@Test
public void testCachingWithDifferentMessages() {
CacheCommand cmd = new CacheCommand();
List<String> tabCompletions1 = cmd.tabComplete("test", "", new String[]{""});
List<String> tabCompletions2 = cmd.tabComplete("test", "", new String[]{"0"});
assertThat(tabCompletions1, is(equalTo(tabCompletions2)));
}
@Test
public void testCachingWithDifferentSenders() {
CacheCommand cmd = new CacheCommand();

Datei anzeigen

@ -0,0 +1,59 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestSWCommand;
import de.steamwar.command.dto.TestTypeMapper;
import java.util.Collection;
public class NullMapperCommand extends TestSWCommand {
public NullMapperCommand() {
super("nullmapper");
}
@Register
public void test(String sender, @AllowNull String arg) {
if (arg == null) {
throw new ExecutionIdentifier("null");
}
throw new ExecutionIdentifier("notnull");
}
@ClassMapper(value = String.class, local = true)
public TestTypeMapper<String> typeMapper() {
return new TestTypeMapper<String>() {
@Override
public String map(String sender, PreviousArguments previousArguments, String s) {
if (s.equals("Hello World")) {
return null;
}
return s;
}
@Override
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) {
return null;
}
};
}
}

Datei anzeigen

@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -24,29 +24,29 @@ import org.junit.Test;
import static de.steamwar.AssertionUtils.assertCMDFramework;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
public class GuardCommandTest {
public class NullMapperCommandTest {
@Test
public void test() {
GuardCommand cmd = new GuardCommand();
public void testNull() {
NullMapperCommand command = new NullMapperCommand();
try {
cmd.execute("test", "", new String[0]);
command.execute("test", "", new String[] {"Hello World"});
assertThat(true, is(false));
} catch (Exception e) {
assertThat(e.getMessage(), is("GuardChecker COMMAND"));
assertCMDFramework(e, ExecutionIdentifier.class, "null");
}
}
@Test
public void testTabComplete() {
GuardCommand cmd = new GuardCommand();
public void testNonNull() {
NullMapperCommand command = new NullMapperCommand();
try {
cmd.tabComplete("test", "", new String[]{""});
command.execute("test", "", new String[] {"Hello"});
assertThat(true, is(false));
} catch (Exception e) {
assertThat(e, is(instanceOf(ExecutionIdentifier.class)));
assertThat(e.getMessage(), is("GuardChecker TAB_COMPLETE"));
assertCMDFramework(e, ExecutionIdentifier.class, "notnull");
}
}
}

Datei anzeigen

@ -0,0 +1,35 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestSWCommand;
public class NumberValidatorCommand extends TestSWCommand {
public NumberValidatorCommand() {
super("numberValidator");
}
@Register
public void test(String sender, @Min(intValue = 0) @Max(intValue = 10) int i) {
throw new ExecutionIdentifier("RunNumberValidator with int");
}
}

Datei anzeigen

@ -0,0 +1,51 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import org.junit.Test;
import static de.steamwar.AssertionUtils.assertCMDFramework;
public class NumberValidatorCommandTest {
@Test
public void testMinValue() {
NumberValidatorCommand command = new NumberValidatorCommand();
command.execute("sender", "", new String[]{"-1"});
}
@Test
public void testMaxValue() {
NumberValidatorCommand command = new NumberValidatorCommand();
command.execute("sender", "", new String[]{"11"});
}
@Test
public void testValidValue() {
try {
NumberValidatorCommand command = new NumberValidatorCommand();
command.execute("sender", "", new String[]{"2"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunNumberValidator with int");
}
}
}

Datei anzeigen

@ -0,0 +1,70 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestSWCommand;
import de.steamwar.command.dto.TestTypeMapper;
import java.util.Arrays;
import java.util.Collection;
public class PartOfCommand {
public static class ParentCommand extends TestSWCommand {
public ParentCommand() {
super("parent");
}
@Register
public void execute(String s, String... args) {
throw new ExecutionIdentifier("ParentCommand with String...");
}
@Mapper("test")
public TestTypeMapper<Integer> typeMapper() {
return new TestTypeMapper<Integer>() {
@Override
public Integer map(String sender, PreviousArguments previousArguments, String s) {
return -1;
}
@Override
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) {
return Arrays.asList(s);
}
};
}
}
@AbstractSWCommand.PartOf(ParentCommand.class)
public static class SubCommand extends TestSWCommand {
public SubCommand() {
super(null);
}
@Register
public void execute(String s, @Mapper("test") int i) {
throw new ExecutionIdentifier("SubCommand with int " + i);
}
}
}

Datei anzeigen

@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@ -20,24 +20,23 @@
package de.steamwar.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestGuardChecker;
import de.steamwar.command.dto.TestSWCommand;
import org.junit.Test;
public class GuardCommand extends TestSWCommand {
import static de.steamwar.AssertionUtils.assertCMDFramework;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
public GuardCommand() {
super("typemapper");
public class PartOfCommandTest {
@Test
public void testMerging() {
PartOfCommand.ParentCommand command = new PartOfCommand.ParentCommand();
new PartOfCommand.SubCommand();
try {
command.execute("test", "", new String[]{"0"});
assertThat(true, is(false));
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "SubCommand with int -1");
}
@Register
public void test(@Guard String sender) {
throw new ExecutionIdentifier("RunTypeMapper");
}
@ClassGuard(value = String.class, local = true)
public AbstractGuardChecker<String> getGuardChecker() {
return (TestGuardChecker) (s, guardCheckType, previousArguments, s2) -> {
throw new ExecutionIdentifier("GuardChecker " + guardCheckType);
};
}
}

Datei anzeigen

@ -0,0 +1,54 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestSWCommand;
import de.steamwar.command.dto.TestTypeMapper;
import java.util.Arrays;
import java.util.Collection;
public class PreviousArgumentCommand extends TestSWCommand {
public PreviousArgumentCommand() {
super("previous");
}
@Register
public void genericCommand(String s, int i, String l) {
throw new ExecutionIdentifier(l);
}
@ClassMapper(value = String.class, local = true)
public TestTypeMapper<String> typeMapper() {
return new TestTypeMapper<String>() {
@Override
public String map(String sender, PreviousArguments previousArguments, String s) {
return "RunTypeMapper_" + previousArguments.getMappedArg(0) + "_" + previousArguments.getMappedArg(0).getClass().getSimpleName();
}
@Override
public Collection<String> tabCompletes(String sender, PreviousArguments previousArguments, String s) {
return Arrays.asList(previousArguments.getMappedArg(0) + "");
}
};
}
}

Datei anzeigen

@ -0,0 +1,56 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import org.junit.Test;
import java.util.List;
import static de.steamwar.AssertionUtils.assertCMDFramework;
import static de.steamwar.AssertionUtils.assertTabCompletes;
public class PreviousArgumentCommandTest {
@Test
public void testPrevArg1() {
PreviousArgumentCommand command = new PreviousArgumentCommand();
List<String> strings = command.tabComplete("", "", new String[]{"1", ""});
assertTabCompletes(strings, "1");
}
@Test
public void testPrevArg2() {
PreviousArgumentCommand command = new PreviousArgumentCommand();
List<String> strings = command.tabComplete("", "", new String[]{"2", ""});
assertTabCompletes(strings, "2");
}
@Test
public void testPrevArgExecute() {
PreviousArgumentCommand command = new PreviousArgumentCommand();
try {
command.execute("", "", new String[]{"2", "2"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunTypeMapper_2_Integer");
}
}
}

Datei anzeigen

@ -28,6 +28,11 @@ public class SimpleCommand extends TestSWCommand {
super("simple");
}
@Register(value = "a", noTabComplete = true)
public void test(String s, String... varargs) {
throw new ExecutionIdentifier("RunSimple with Varargs");
}
@Register
public void simple(String s) {
throw new ExecutionIdentifier("RunSimple with noArgs");

Datei anzeigen

@ -39,6 +39,16 @@ public class SimpleCommandTest {
}
}
@Test
public void testVarArgs() {
SimpleCommand cmd = new SimpleCommand();
try {
cmd.execute("test", "", new String[] {"a", "b", "c"});
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunSimple with Varargs");
}
}
@Test
public void testSimpleParsingNoResult() {
SimpleCommand cmd = new SimpleCommand();

Datei anzeigen

@ -0,0 +1,55 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestSWCommand;
public class StaticValueCommand extends TestSWCommand {
public StaticValueCommand() {
super("staticvalue");
}
@Register
public void defaultStaticValue(String s, @StaticValue({"hello", "world"}) String staticValue) {
throw new ExecutionIdentifier("RunStaticValue with " + staticValue);
}
@Register
public void booleanStaticValue(String s, @StaticValue(value = {"-a", "-b", "-c"}, allowISE = true) boolean staticValue) {
throw new ExecutionIdentifier("RunStaticValue with " + staticValue);
}
@Register
public void booleanStaticValueOtherFalseValue(String s, @StaticValue(value = {"-d", "-e", "-f"}, allowISE = true, falseValues = { 1 }) boolean staticValue) {
throw new ExecutionIdentifier("RunStaticValue with " + staticValue);
}
@Register
public void intStaticValue(String s, @StaticValue(value = {"-g", "-h", "-i"}, allowISE = true) int staticValue) {
throw new ExecutionIdentifier("RunStaticValue with int " + staticValue);
}
@Register
public void longStaticValue(String s, @StaticValue(value = {"-j", "-k", "-l"}, allowISE = true) long staticValue) {
throw new ExecutionIdentifier("RunStaticValue with long " + staticValue);
}
}

Datei anzeigen

@ -0,0 +1,147 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import org.junit.Test;
import java.util.List;
import static de.steamwar.AssertionUtils.assertCMDFramework;
import static de.steamwar.AssertionUtils.assertTabCompletes;
public class StaticValueCommandTest {
@Test
public void tabCompletionTest() {
StaticValueCommand cmd = new StaticValueCommand();
List<String> strings = cmd.tabComplete("", "", new String[]{""});
assertTabCompletes(strings, "hello", "world", "-a", "-b", "-c", "-d", "-e", "-f", "-g", "-h", "-i", "-j", "-k", "-l");
}
@Test
public void defaultTest() {
StaticValueCommand cmd = new StaticValueCommand();
try {
cmd.execute("", "", new String[] {"hello"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with hello");
}
try {
cmd.execute("", "", new String[] {"world"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with world");
}
}
@Test
public void booleanTest() {
StaticValueCommand cmd = new StaticValueCommand();
try {
cmd.execute("", "", new String[] {"-a"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with false");
}
try {
cmd.execute("", "", new String[] {"-b"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true");
}
try {
cmd.execute("", "", new String[] {"-c"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true");
}
}
@Test
public void booleanOtherFalseTest() {
StaticValueCommand cmd = new StaticValueCommand();
try {
cmd.execute("", "", new String[] {"-d"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true");
}
try {
cmd.execute("", "", new String[] {"-e"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with false");
}
try {
cmd.execute("", "", new String[] {"-f"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with true");
}
}
@Test
public void intTest() {
StaticValueCommand cmd = new StaticValueCommand();
try {
cmd.execute("", "", new String[] {"-g"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with int 0");
}
try {
cmd.execute("", "", new String[] {"-h"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with int 1");
}
try {
cmd.execute("", "", new String[] {"-i"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with int 2");
}
}
@Test
public void longTest() {
StaticValueCommand cmd = new StaticValueCommand();
try {
cmd.execute("", "", new String[] {"-j"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with long 0");
}
try {
cmd.execute("", "", new String[] {"-k"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with long 1");
}
try {
cmd.execute("", "", new String[] {"-l"});
assert false;
} catch (Exception e) {
assertCMDFramework(e, ExecutionIdentifier.class, "RunStaticValue with long 2");
}
}
}

Datei anzeigen

@ -0,0 +1,58 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.command;
import de.steamwar.command.dto.ExecutionIdentifier;
import de.steamwar.command.dto.TestSWCommand;
import de.steamwar.command.dto.TestTypeMapper;
import java.util.Collections;
public class SubCMDSortingCommand extends TestSWCommand {
public SubCMDSortingCommand() {
super("subcmdsorting");
}
@Register
public void test(String s) {
throw new ExecutionIdentifier("Command with 0 parameters");
}
@Register
public void test(String s, String args) {
throw new ExecutionIdentifier("Command with 1 parameter");
}
@Register
public void test(String s, String i1, String i2, String i3, String... args) {
throw new ExecutionIdentifier("Command with 3+n parameters");
}
@Register
public void test(String s, String... args) {
throw new ExecutionIdentifier("Command with n parameters");
}
@ClassMapper(value = String.class, local = true)
public AbstractTypeMapper<String, String> stringMapper() {
return SWCommandUtils.createMapper(s -> s, Collections::singletonList);
}
}

Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen