Compare commits

...

145 Commits

Author SHA1 Message Date
Chaoscaot b97fe7e5b1 Merge branch 'main' into RemoveVersionCode
Pull Request Build / Build (pull_request) Successful in 1m18s
Backport CommonCore / Create CommonCore backport PRs (pull_request) Successful in 7s
2026-05-15 16:05:42 +02:00
YoyoNow 01db4fa951 Fix CraftbukkitWrapper for FightSystem
Pull Request Build / Build (pull_request) Successful in 1m19s
2026-05-15 16:00:50 +02:00
Chaoscaot cb153b50f1 Merge remote-tracking branch 'origin/main'
Deploy / Build (push) Successful in 1m31s
Deploy / Deploy (push) Successful in 9s
2026-05-15 15:51:06 +02:00
Chaoscaot 8b33bf40c3 Add CLI artifact deployment and service restart steps in build workflow
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 15:51:01 +02:00
YoyoNow 9a6221b723 Fix DiscordChannel
Deploy / Build (push) Successful in 1m31s
Deploy / Deploy (push) Successful in 8s
2026-05-15 15:44:27 +02:00
Chaoscaot a20b1cb263 Disable blank issues in Gitea configuration
Deploy / Build (push) Successful in 1m38s
Deploy / Deploy (push) Successful in 8s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 15:39:04 +02:00
Chaoscaot cfe6055083 Disable blank issues in Gitea configuration
Deploy / Build (push) Has started running
Deploy / Deploy (push) Has been cancelled
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 15:38:11 +02:00
Chaoscaot eea9abdc56 Add Templates
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 15:37:29 +02:00
Chaoscaot 138b94e562 Merge pull request 'Test Backporting' (#354) from test/backporting into main
Deploy / Build (push) Successful in 3m22s
Deploy / Deploy (push) Successful in 7s
Reviewed-on: #354
2026-05-15 15:09:46 +02:00
Chaoscaot d11467bd1b Test Backporting
Backport CommonCore / Create CommonCore backport PRs (pull_request) Successful in 15s
Pull Request Build / Build (pull_request) Successful in 2m44s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 15:09:28 +02:00
Chaoscaot 7be40d9bf9 Merge pull request 'Test Backporting' (#352) from test/backporting into main
Deploy / Build (push) Successful in 2m1s
Deploy / Deploy (push) Successful in 11s
Reviewed-on: #352
2026-05-15 15:03:40 +02:00
Chaoscaot dae8073992 Test Backporting
Backport CommonCore / Create CommonCore backport PRs (pull_request) Successful in 11s
Pull Request Build / Build (pull_request) Successful in 2m15s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 15:03:22 +02:00
Chaoscaot 143e7dc17c Test Backporting
Deploy / Deploy (push) Has been cancelled
Deploy / Build (push) Has been cancelled
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 15:03:01 +02:00
Chaoscaot f4ace64173 Merge pull request 'Test Backporting' (#351) from test/backporting into main
Deploy / Build (push) Successful in 2m30s
Deploy / Deploy (push) Successful in 7s
Reviewed-on: #351
2026-05-15 14:55:44 +02:00
Chaoscaot 236b486845 Test Backporting
Backport CommonCore / Create CommonCore backport PRs (pull_request) Failing after 14s
Pull Request Build / Build (pull_request) Successful in 2m13s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 14:55:13 +02:00
Chaoscaot c666f0228d Test Backporting
Deploy / Deploy (push) Has been cancelled
Deploy / Build (push) Has been cancelled
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 14:54:31 +02:00
YoyoNow 81049856b5 Fix build
Pull Request Build / Build (pull_request) Successful in 1m38s
2026-05-15 14:52:17 +02:00
YoyoNow 4813e24848 Remove LegacyBauSystem 2026-05-15 14:52:17 +02:00
YoyoNow d61d001ddf Cleanup FightSystem 2026-05-15 14:52:17 +02:00
YoyoNow 060364abb5 Cleanup SpigotCore 2026-05-15 14:52:17 +02:00
YoyoNow 13185f0e05 Cleanup SchematicSystem 2026-05-15 14:52:17 +02:00
YoyoNow fd266969f5 Cleanup BauSystem 2026-05-15 14:52:17 +02:00
Chaoscaot bd471330e1 Merge pull request 'Test Backporting' (#350) from test/backporting into main
Deploy / Build (push) Successful in 3m31s
Deploy / Deploy (push) Successful in 19s
Reviewed-on: #350
2026-05-15 14:48:42 +02:00
Chaoscaot 92602efa9e Test Backporting
Backport CommonCore / Create CommonCore backport PRs (pull_request) Failing after 10s
Pull Request Build / Build (pull_request) Successful in 2m45s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 14:48:13 +02:00
Chaoscaot 55a39ac85a Merge remote-tracking branch 'origin/main'
Deploy / Deploy (push) Has been cancelled
Deploy / Build (push) Has been cancelled
2026-05-15 14:47:41 +02:00
Chaoscaot 39898825ef Test Backporting
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 14:47:37 +02:00
Chaoscaot 59a2d1454e Merge pull request 'Test Backporting' (#349) from test/backporting into main
Deploy / Build (push) Successful in 2m28s
Deploy / Deploy (push) Successful in 11s
Reviewed-on: #349
2026-05-15 14:41:44 +02:00
Chaoscaot 16c324cf32 Test Backporting
Backport CommonCore / Create CommonCore backport PRs (pull_request) Successful in 15s
Pull Request Build / Build (pull_request) Successful in 2m2s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 14:41:25 +02:00
Chaoscaot b923b98b2c Test Backporting
Deploy / Deploy (push) Has been cancelled
Deploy / Build (push) Has been cancelled
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 14:41:06 +02:00
Chaoscaot afca7d5135 Merge pull request 'Test Backporting' (#348) from test/backporting into main
Deploy / Build (push) Successful in 1m31s
Deploy / Deploy (push) Successful in 8s
Reviewed-on: #348
2026-05-15 14:37:22 +02:00
Chaoscaot a05bebcd81 Test Backporting
Pull Request Build / Build (pull_request) Successful in 1m36s
Backport CommonCore / Create CommonCore backport PRs (pull_request) Failing after 11s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 14:35:30 +02:00
Chaoscaot 02cc8330ca Add automated CommonCore backport workflow
Deploy / Build (push) Successful in 1m46s
Deploy / Deploy (push) Successful in 11s
- Create backport PRs for merged CommonCore changes on `main`
- Auto-merge successful backport PRs into `version/*` branches
- Add opt-out label handling for backports
2026-05-15 14:35:02 +02:00
Chaoscaot f8397b8bab Test Backporting
Deploy / Build (push) Successful in 2m6s
Deploy / Deploy (push) Successful in 7s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 14:15:35 +02:00
Chaoscaot 38603fbe45 Merge pull request 'Test Backporting' (#347) from test/backporting into main
Deploy / Deploy (push) Has been cancelled
Deploy / Build (push) Has been cancelled
Reviewed-on: #347
2026-05-15 14:14:15 +02:00
Chaoscaot 78700e868d Test Backporting
Backport CommonCore / Backport CommonCore changes (pull_request) Failing after 11s
Pull Request Build / Build (pull_request) Successful in 2m15s
Pull Request Build / Merge backport (pull_request) Has been skipped
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 14:14:04 +02:00
Chaoscaot 259e8bdb7b Test Backporting
Deploy / Deploy (push) Has been cancelled
Deploy / Build (push) Has been cancelled
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 14:13:41 +02:00
Chaoscaot 0ea3c25b7b Merge pull request 'Test Backporting' (#346) from test/backporting into main
Deploy / Build (push) Successful in 2m48s
Deploy / Deploy (push) Successful in 8s
Reviewed-on: #346
2026-05-15 14:02:39 +02:00
Chaoscaot 2fa1b7d329 Test Backporting
Backport CommonCore / Backport CommonCore changes (pull_request) Failing after 10s
Pull Request Build / Build (pull_request) Successful in 2m25s
Pull Request Build / Merge backport (pull_request) Has been skipped
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 14:02:24 +02:00
Chaoscaot af805f6ba4 Merge pull request 'Test Backporting' (#345) from test/backporting into main
Deploy / Deploy (push) Has been cancelled
Deploy / Build (push) Has been cancelled
Reviewed-on: #345
2026-05-15 14:01:41 +02:00
Chaoscaot 34c361d3f8 Test Backporting
Backport CommonCore / Backport CommonCore changes (pull_request) Failing after 8s
Pull Request Build / Build (pull_request) Successful in 2m17s
Pull Request Build / Merge backport (pull_request) Has been skipped
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 14:01:22 +02:00
Chaoscaot 83eb621b0f Merge pull request 'Test Backporting' (#344) from test/backporting into main
Deploy / Build (push) Successful in 1m30s
Deploy / Deploy (push) Successful in 7s
Reviewed-on: #344
2026-05-15 13:58:00 +02:00
Chaoscaot 65aaf4857d Test Backporting
Pull Request Build / Build (pull_request) Successful in 2m8s
Pull Request Build / Merge backport (pull_request) Has been skipped
Backport CommonCore / Backport CommonCore changes (pull_request) Failing after 8s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 13:55:12 +02:00
Chaoscaot 7d52447b00 Merge pull request 'Test Backporting' (#343) from test/backporting into main
Deploy / Build (push) Successful in 3m20s
Deploy / Deploy (push) Successful in 9s
Reviewed-on: #343
2026-05-15 13:54:00 +02:00
Chaoscaot 046ab8d1a8 Merge branch 'main' into test/backporting
Backport CommonCore / Backport CommonCore changes (pull_request) Failing after 12s
Pull Request Build / Build (pull_request) Successful in 2m49s
Pull Request Build / Merge backport (pull_request) Has been skipped
2026-05-15 13:53:53 +02:00
Chaoscaot 33c032092d Test Backporting
Pull Request Build / Build (pull_request) Successful in 2m7s
Pull Request Build / Merge backport (pull_request) Has been skipped
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 13:53:09 +02:00
Chaoscaot b8eeb93a8f Merge pull request 'Test Backporting' (#342) from test/backporting into main
Deploy / Build (push) Successful in 2m26s
Deploy / Deploy (push) Successful in 7s
Reviewed-on: #342
2026-05-15 13:44:41 +02:00
Chaoscaot e95f68406f Test Backporting
Backport CommonCore / Backport CommonCore changes (pull_request) Failing after 16s
Pull Request Build / Build (pull_request) Successful in 2m14s
Pull Request Build / Merge backport (pull_request) Has been skipped
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 13:44:23 +02:00
Chaoscaot c6f432a5c4 Test Backporting
Deploy / Deploy (push) Has been cancelled
Deploy / Build (push) Has been cancelled
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 13:43:56 +02:00
Chaoscaot 9ca7446dba .gitea/workflows/backport-commoncore.yml aktualisiert
Deploy / Build (push) Has started running
Deploy / Deploy (push) Has been cancelled
2026-05-15 13:42:26 +02:00
Chaoscaot 773a1adf64 Merge pull request 'Test Backporting' (#341) from test/backporting into main
Deploy / Build (push) Successful in 1m32s
Deploy / Deploy (push) Successful in 8s
Reviewed-on: #341
2026-05-15 13:38:23 +02:00
Chaoscaot 6afe5d4c0d Test Backporting
Pull Request Build / Build (pull_request) Successful in 1m11s
Pull Request Build / Merge backport (pull_request) Has been skipped
Backport CommonCore / Backport CommonCore changes (pull_request) Failing after 13s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 13:31:49 +02:00
Chaoscaot 755a05fe34 Add merge-backport workflow to automate PR backports after successful builds
Deploy / Build (push) Successful in 1m28s
Deploy / Deploy (push) Successful in 7s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 13:31:11 +02:00
Chaoscaot 202da658ee Refactor workflows: split pull request build, rename deploy, and remove unused steps
Deploy / Build (push) Successful in 1m31s
Deploy / Deploy (push) Successful in 7s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 12:47:00 +02:00
Chaoscaot a5856cf240 Merge pull request 'Test Autobackporting' (#340) from test/backporting into main
Build / Build (push) Successful in 1m37s
Build / Deploy (push) Successful in 7s
Build / Merge backport (push) Has been skipped
Reviewed-on: #340
2026-05-15 12:31:37 +02:00
Chaoscaot 38099e6167 Test Autobackporting
Build / Build (push) Successful in 1m12s
Build / Build (pull_request) Successful in 1m12s
Build / Deploy (push) Has been skipped
Build / Merge backport (push) Has been skipped
Build / Deploy (pull_request) Has been skipped
Build / Merge backport (pull_request) Has been skipped
Backport CommonCore / Backport CommonCore changes (pull_request) Failing after 7s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 12:28:10 +02:00
Chaoscaot d307038e5e Use non basic caching
Build / Build (push) Successful in 3m21s
Build / Deploy (push) Successful in 7s
Build / Merge backport (push) Has been skipped
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 12:07:41 +02:00
Chaoscaot d5aeeaf5e3 Add backport workflow for CommonCore changes and update build.yml
Build / Deploy (push) Has been cancelled
Build / Merge backport (push) Has been cancelled
Build / Build (push) Has been cancelled
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 12:04:02 +02:00
Chaoscaot 134a05ea23 Add build.yml
Build / Build (push) Successful in 3m8s
Build / Deploy (push) Successful in 9s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 11:37:16 +02:00
Chaoscaot db63f2a67c Add build.yml
Build / Build (push) Failing after 3m45s
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 11:29:08 +02:00
Chaoscaot 97a3c3ef35 .gitea/workflows/build.yml aktualisiert
/ Build (push) Successful in 3m54s
2026-05-15 11:09:37 +02:00
Chaoscaot a40e904ab3 .gitea/workflows/build.yml aktualisiert
/ Build (push) Failing after 2m0s
2026-05-15 11:06:54 +02:00
Chaoscaot a8a89b0809 .gitea/workflows/build.yml aktualisiert
/ Build (push) Failing after 5m8s
2026-05-15 10:30:13 +02:00
Chaoscaot 480efd1f8b Add build.yml
/ Build (push) Has been cancelled
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-15 10:11:29 +02:00
YoyoNow 26baccf3c4 Test push 2026-05-15 10:09:02 +02:00
Chaoscaot 916f9b2557 Merge pull request 'SteamWar CLI merge' (#330) from feature/steamwar-cli into main
SteamWarCI Build successful
Reviewed-on: #330
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
2026-05-13 22:42:59 +02:00
YoyoNow 34992344b2 Fix CheckCommand.accept
SteamWarCI Build successful
2026-05-12 16:47:30 +02:00
YoyoNow 1ea8dea381 Fix WhoisCommand.whois
SteamWarCI Build successful
2026-05-11 21:03:51 +02:00
Chaoscaot 15aa0572f3 Merge pull request 'Implement old bau behavior for WGS' (#332) from old-bau-behavior into main
SteamWarCI Build successful
Reviewed-on: #332
Reviewed-by: D4rkr34lm <dark@steamwar.de>
2026-05-10 18:55:25 +02:00
Chaoscaot 6a843f4a71 Implement old bau behavior for WGS
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-10 11:29:26 +02:00
Chaoscaot a63c1a94ca Fix TNT explosion logic to handle non-TNT entities correctly
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-09 21:10:43 +02:00
Chaoscaot 43263035d9 Merge pull request 'Add DB Indexes for future local Development deployments' (#262) from add-db-indexes into main
SteamWarCI Build successful
Reviewed-on: #262
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
2026-05-09 20:23:56 +02:00
Chaoscaot 42ab55d0f8 Merge branch 'main' into add-db-indexes
SteamWarCI Build successful
# Conflicts:
#	CommonCore/SQL/src/de/steamwar/sql/BannedUserIPs.kt
2026-05-09 20:22:15 +02:00
Chaoscaot 44846cce57 Unpack CLI distribution in release
SteamWarCI Build successful
- Remove CLI installDist from build step
- Copy the packaged sw.zip into /jars during release
- Use `rm -rf` for the existing `/jars/sw` cleanup
2026-05-09 16:51:04 +02:00
Chaoscaot 1451750bcb Add SteamWar CLI module
SteamWarCI Build successful
- add Clikt-based `sw` entrypoint and subcommands
- include database, user, dev, and profiler commands
- wire CLI build and CI install/release steps
2026-05-09 16:46:59 +02:00
YoyoNow 8ade5180cb Fix FightSystem
SteamWarCI Build successful
2026-05-08 20:55:17 +02:00
Chaoscaot d0535d0c47 Merge pull request '[fightsystem]: Fix broken kit system' (#322) from bugfix/fightsystem-broken-kits into main
SteamWarCI Build successful
Reviewed-on: #322
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-05-04 18:31:25 +02:00
zOnlyKroks 79fa09e39b Chaos zufrieden stellen
SteamWarCI Build successful
2026-05-04 18:27:28 +02:00
Chaoscaot 4010c2125c Refactor lazy loading of dependents and relations to return lists
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-04 13:41:07 +02:00
zOnlyKroks 32de0077de [fightsystem]: Rework blacklist to whitelist
SteamWarCI Build successful
2026-05-03 14:15:59 +02:00
Chaoscaot bd53e016c5 Merge pull request 'feat: Add highligh flag for tracer' (#320) from BauSystem/add-highlight-trace-flag into main
SteamWarCI Build successful
Reviewed-on: #320
Reviewed-by: zOnlyKroks <zonlyknox@gmail.com>
2026-05-03 13:04:37 +02:00
zOnlyKroks 5b1ed644d1 [fightsystem]: Fix broken kit system
SteamWarCI Build successful
2026-05-03 12:52:23 +02:00
D4rkr34lm a41787d89d Add flag
SteamWarCI Build successful
2026-05-02 13:36:32 +02:00
Chaoscaot 8e392b56c3 Merge pull request 'fix: windcharge check' (#318) from fix/mssing-string-get into main
SteamWarCI Build successful
Reviewed-on: #318
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-05-01 12:30:42 +02:00
D4rkr34lm 42feadcd2d Add left out name get
SteamWarCI Build successful
2026-05-01 11:53:57 +02:00
Chaoscaot 15f0344416 Merge pull request 'feat: Add temporary hardcoded windcharge check to autockecker for WGS' (#316) from fix/enable-windcharges-for-wgs into main
SteamWarCI Build successful
Reviewed-on: #316
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-05-01 11:03:38 +02:00
D4rkr34lm c90a977ab2 use same hacky impl as in fight system
SteamWarCI Build successful
2026-05-01 11:02:33 +02:00
D4rkr34lm b186228f4e Revert "swap out used api"
SteamWarCI Build successful
This reverts commit 5f38474809.
2026-05-01 10:59:23 +02:00
D4rkr34lm 5f38474809 swap out used api
SteamWarCI Build failed
2026-05-01 10:54:53 +02:00
Manuel Frohn 4f27320548 Implement hardcoded windcharge check
SteamWarCI Build successful
2026-04-30 14:44:54 +02:00
D4rkr34lm ba7bd1f1dd Configure V21 impl
SteamWarCI Build failed
2026-04-30 12:52:46 +02:00
YoyoNow fbe70e7ead Improve CheckCommand for WGS
SteamWarCI Build successful
2026-04-24 10:09:56 +02:00
YoyoNow 30a499be1d Hotfix CheckCommand
SteamWarCI Build successful
Remove uneeded stuff in SQLWrapper
2026-04-23 23:33:02 +02:00
Chaoscaot 86e212fe42 Fix Kits
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-04-23 18:23:13 +02:00
YoyoNow 4a646e6be0 Improve WindchargeStopper
SteamWarCI Build successful
2026-04-23 12:09:17 +02:00
YoyoNow bc0dc1925e Merge pull request 'Add WindchargeStopper to handle wind charge entity removal based on fight boundaries' (#182) from windcharges into main
SteamWarCI Build successful
Reviewed-on: #182
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
2026-04-23 12:04:34 +02:00
YoyoNow c3af4dbc68 Merge pull request 'Add CreateKitCommand' (#271) from add-kit-command into main
SteamWarCI Build successful
Reviewed-on: #271
2026-04-23 12:01:18 +02:00
YoyoNow 703639537d Fix CreateKitCommand
SteamWarCI Build failed
2026-04-23 11:59:28 +02:00
YoyoNow 9ac3bf6a6c Redesign GameModeConfig and SchematicType
SteamWarCI Build successful
2026-04-23 11:54:50 +02:00
YoyoNow 41ea6c9407 Fix ViewFlag.ADVANCED
SteamWarCI Build successful
2026-04-23 08:27:43 +02:00
YoyoNow 67e9a3544e Fix Tablist.disable removing gm knowledge
SteamWarCI Build successful
2026-04-20 13:45:16 +02:00
YoyoNow f69ae3e77b Fix BauLock
SteamWarCI Build successful
2026-04-20 13:31:45 +02:00
Chaoscaot 2208dcc0fb Fix Set Parent
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-04-15 18:20:07 +02:00
YoyoNow b466216b3a Fix BlockFormListener
SteamWarCI Build successful
2026-04-13 20:33:11 +02:00
YoyoNow 5a862b251b Add BlockFormListener
SteamWarCI Build successful
2026-04-13 20:29:47 +02:00
YoyoNow 60a82a685d Improve FightSystem REDUCED_DEBUG_INFO on Test Arena
SteamWarCI Build successful
2026-04-13 20:18:43 +02:00
YoyoNow 573b0c14ae Improve FightSystem REDUCED_DEBUG_INFO on Test Arena
SteamWarCI Build successful
2026-04-13 20:03:41 +02:00
YoyoNow 82abe7e20f Fix OutsideWincondition
SteamWarCI Build successful
2026-04-05 12:34:11 +02:00
YoyoNow 34da59714e Fix IngameListener and StartCommand
SteamWarCI Build successful
2026-04-05 12:30:55 +02:00
YoyoNow 97071165cd Fix IngameListener, OutsideWincondition, TowerGenerator
SteamWarCI Build successful
2026-04-05 12:08:29 +02:00
YoyoNow 634465fbf1 Fix BanListener inserting bedrock ips
SteamWarCI Build successful
2026-04-04 12:08:17 +02:00
YoyoNow 2ad8cc3f4a Merge branch 'RemoveUnusedSQL'
SteamWarCI Build successful
2026-04-02 09:01:20 +02:00
YoyoNow e190fe0858 Fix FreezeListener
SteamWarCI Build successful
2026-04-01 19:43:31 +02:00
YoyoNow 569d91a0d3 Remove SelectAdjacent as it annoys most players
SteamWarCI Build successful
2026-03-29 14:15:01 +02:00
YoyoNow 487a15849a Add supress warnings
SteamWarCI Build successful
2026-03-29 13:12:09 +02:00
YoyoNow e110033315 Fix Replays for 1.21
SteamWarCI Build successful
2026-03-29 13:05:41 +02:00
YoyoNow c0b192e2bf Fix WorldEditWrapper not loading schematics in 1.21
SteamWarCI Build successful
2026-03-29 12:53:55 +02:00
YoyoNow 612254296c Fix WorldEditWrapper21
SteamWarCI Build successful
2026-03-29 11:55:16 +02:00
Chaoscaot 1dbcb122c2 Change Loader to use FastSchematicReaderV3
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-03-28 22:30:01 +01:00
Chaoscaot f2ee9dbeb3 Fix Schematic Tabcomplete
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-03-28 22:24:07 +01:00
YoyoNow 404ab2abfb Update GDPRQuery
SteamWarCI Build successful
2026-03-25 07:46:38 +01:00
YoyoNow 59a927c33c Remove Team.address and Team.port
SteamWarCI Build successful
Remove PollAnswer and UserElo from GDPRQuery
2026-03-24 22:11:19 +01:00
Chaoscaot 6c062216a1 Remove usage of EffectiveSchematicNode
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-03-24 20:07:17 +01:00
YoyoNow 72d62dfbe5 Fix AutoCheckerResult ignoring Water and Lava correctly
SteamWarCI Build successful
2026-03-22 15:23:46 +01:00
YoyoNow 76ecaccc41 Hotfix AutoChecker15 for now
SteamWarCI Build successful
2026-03-21 17:45:26 +01:00
YoyoNow 9587b9e1fd Fix Laufbau double click
SteamWarCI Build successful
2026-03-21 09:46:20 +01:00
YoyoNow 14dc807fd9 Fix SoulSand in LaufbauCommand
SteamWarCI Build successful
2026-03-21 09:39:17 +01:00
YoyoNow 63ad85f727 Fix TickManager21.stepTicks
SteamWarCI Build successful
2026-03-21 09:37:05 +01:00
YoyoNow 72e88502d2 Fix TeamCommand /team event ...
SteamWarCI Build successful
2026-03-21 09:26:02 +01:00
YoyoNow 71767ef6d9 Fix TeamCommand /team event ...
SteamWarCI Build successful
2026-03-21 09:20:57 +01:00
YoyoNow 5e19629df5 Fix build
SteamWarCI Build successful
2026-03-15 12:54:43 +01:00
YoyoNow ca70c6685c Remove Lunar client support
SteamWarCI Build failed
They currently have a problem with their maven repository
2026-03-15 12:52:56 +01:00
YoyoNow f00bd153fe Add GameModeConfig#Schematic#ReplacementsWithoutBlockUpdates
SteamWarCI Build failed
Add GameModeConfig#Schematic#ReplacementsWithBlockUpdates
2026-03-15 12:49:13 +01:00
YoyoNow c1221e5cf5 Remove SWTSI (SteamWar Teamserver Integration)
SteamWarCI Build failed
2026-03-13 21:16:13 +01:00
YoyoNow 236944ff69 Remove useless System.out
SteamWarCI Build failed
2026-03-13 21:12:34 +01:00
YoyoNow ab85c72fe3 Fix DesignEndStone
SteamWarCI Build failed
Closes: #292
Closes: #288
2026-03-13 21:08:00 +01:00
YoyoNow a750185df0 Fix stop not working for DevServer starter
SteamWarCI Build successful
2026-03-02 12:10:18 +01:00
YoyoNow 008ff1091f Hotfix DevCommand
SteamWarCI Build successful
2026-03-02 11:59:52 +01:00
YoyoNow 5d24581038 Fix WaterRemover.handleEntityExplode
SteamWarCI Build successful
2026-03-01 21:47:22 +01:00
YoyoNow bce07a4ac8 Add GameModeConfig.ArenaConfig.WaterDamage for hard water damage by setting air or normal handling
SteamWarCI Build successful
2026-03-01 21:36:29 +01:00
Chaoscaot 30b7bbc283 Fix WebPW Command
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-02-09 09:28:14 +01:00
Chaoscaot 46a11af6ca Fix Ban Command
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-02-02 19:12:34 +01:00
Chaoscaot 73f903fc23 Merge branch 'main' into add-db-indexes
SteamWarCI Build successful
2026-01-23 23:04:11 +01:00
Chaoscaot 25116c3865 Add CreateKitCommand
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-01-03 02:30:36 +01:00
Chaoscaot 22ed7e23da Add DB Indexes for future local Development deployments
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2025-12-25 20:51:57 +01:00
Chaoscaot c0163d813e Add WindchargeStopper to handle wind charge entity removal based on fight boundaries
SteamWarCI Build successful
Signed-off-by: Chaoscaot <max@maxsp.de>
2025-10-28 23:00:53 +01:00
443 changed files with 4335 additions and 22240 deletions
+60
View File
@@ -0,0 +1,60 @@
name: Bug Report
about: Du hast einen Fehler gefunden? Melde ihn hier!
labels: [ "typ/bug" ]
body:
- type: markdown
attributes:
value: |
ACHTUNG: Sollte es bei dem Bug ein Sicherheitsrisiko geben, melde es bitte auf unserem Discord Server
- type: textarea
id: description
attributes:
label: Description
description: |
Beschreibe deinen Bug in kurzer Form.
- type: input
id: mc-ver
attributes:
label: Minecraft Version
description: Minecraft Version des Clients
validations:
required: true
- type: input
id: mc-ver-ser
attributes:
label: Minecraft Version Server
description: Minecraft Version des Servers, nur bei Bau oder Arenen Servern
- type: dropdown
id: can-reproduce
attributes:
label: Kannst du den Fehler wiederholen?
description: |
Wenn du den Fehler wiederholen kannst, können wir dieses Problem schneller beheben.
Solltest du den Fehler nicht wiederholen können, melde dich bitte auf unserem Discord Server.
options:
- "Yes"
- "No"
validations:
required: true
- type: textarea
id: reproduce-steps
attributes:
label: Wie kannst du den Fehler wiederholen?
description: Welche Schritte musst du ausführen, um den Fehler wiederholen zu können?
validations:
required: true
- type: textarea
id: expected-result
attributes:
label: Was sollte passieren?
description: Was sollte hier deiner Erwartung nach passieren?
- type: input
id: logs
attributes:
label: Auf welchem Server ist der Fehler aufgetreten?
description: Gebe bitte den Namen des Servers an, auf dem der Fehler aufgetreten ist. z.B. "Lobby", "Lixfels Bauserver" etc.
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: Sollte es ein Visuelles Problem geben, kannst du hier Screenshots hinzufügen.
+1
View File
@@ -0,0 +1 @@
blank_issues_enabled: true
@@ -0,0 +1,17 @@
name: Feature Idee
about: Du hast eine Idee für ein neues Feature, welches SteamWar nicht hat? Stelle sie hier vor.
labels: ["typ/idee"]
body:
- type: textarea
id: description
attributes:
label: Feature Beschreibung
placeholder: |
Ich glaube, dass ...
validations:
required: true
- type: textarea
id: screenshots
attributes:
label: Screenshots
description: Wenn es sich um etwas grafisches handelt, kannst du hier Screenshots hinzufügen.
+193
View File
@@ -0,0 +1,193 @@
name: Backport CommonCore
on:
pull_request:
types:
- closed
branches:
- main
permissions:
contents: write
pull-requests: write
env:
BACKPORT_PATH: CommonCore
BACKPORT_BRANCH_PREFIX: backport/commoncore
DISABLE_BACKPORT_LABEL: no-backport
jobs:
backport:
name: Create CommonCore backport PRs
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Check backport eligibility
id: eligibility
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.CI_BOT_TOKEN }}
run: |
set -euo pipefail
api_url="${GITHUB_API_URL:-${GITHUB_SERVER_URL}/api/v1}"
echo "Backport debug: event=${GITHUB_EVENT_NAME:-unknown}"
echo "Backport debug: server=${GITHUB_SERVER_URL}"
echo "Backport debug: api=${api_url}"
echo "Backport debug: repository=${GITHUB_REPOSITORY}"
echo "Backport debug: action=$(jq -r '.action // ""' "$GITHUB_EVENT_PATH")"
merged="$(jq -r '.pull_request.merged // (.pull_request.merged_at != null)' "$GITHUB_EVENT_PATH")"
base_branch="$(jq -r '.pull_request.base.ref' "$GITHUB_EVENT_PATH")"
has_disable_label="$(jq -r --arg disable_backport_label "$DISABLE_BACKPORT_LABEL" 'any(.pull_request.labels[]?; .name == $disable_backport_label)' "$GITHUB_EVENT_PATH")"
echo "Backport debug: pr=$(jq -r '.pull_request.number // ""' "$GITHUB_EVENT_PATH") base=${base_branch} merged=${merged}"
echo "Backport debug: disable label present=${has_disable_label}"
{
echo "should_backport=$([[ "$merged" == "true" && "$base_branch" == "main" && "$has_disable_label" != "true" ]] && echo true || echo false)"
echo "pr_number=$(jq -r '.pull_request.number' "$GITHUB_EVENT_PATH")"
echo "pr_title<<EOF"
jq -r '.pull_request.title' "$GITHUB_EVENT_PATH"
echo "EOF"
} >> "$GITHUB_OUTPUT"
labels="$(curl -fsS \
-H "Accept: application/json" \
-H "Authorization: token ${GITHUB_TOKEN}" \
"${api_url}/repos/${GITHUB_REPOSITORY}/labels")"
if ! jq -e --arg disable_backport_label "$DISABLE_BACKPORT_LABEL" 'any(.[]; .name == $disable_backport_label)' <<< "$labels" >/dev/null; then
curl -fsS -X POST \
-H "Accept: application/json" \
-H "Authorization: token ${GITHUB_TOKEN}" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg name "$DISABLE_BACKPORT_LABEL" --arg color "ededed" --arg description "Disable automatic CommonCore backporting for this pull request." '{name: $name, color: $color, description: $description}')" \
"${api_url}/repos/${GITHUB_REPOSITORY}/labels"
fi
- name: Create backport pull requests
if: steps.eligibility.outputs.should_backport == 'true'
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.CI_BOT_TOKEN }}
PR_NUMBER: ${{ steps.eligibility.outputs.pr_number }}
PR_TITLE: ${{ steps.eligibility.outputs.pr_title }}
run: |
set -euo pipefail
api_url="${GITHUB_API_URL:-${GITHUB_SERVER_URL}/api/v1}"
repo_api_path="/repos/${GITHUB_REPOSITORY}"
api_request() {
local method="$1"
local path="$2"
local output="$3"
local data_file="${4:-}"
local status
local args=(-sS -X "$method" -H "Accept: application/json" -H "Authorization: token ${GITHUB_TOKEN}" -w "%{http_code}" -o "$output")
if [[ -n "$data_file" ]]; then
args+=(-H "Content-Type: application/json" --data-binary "@${data_file}")
fi
echo "Backport debug: ${method} ${path}"
if ! status="$(curl "${args[@]}" "${api_url}${path}")"; then
echo "Backport debug: ${method} ${path} failed before HTTP status was captured."
if [[ -s "$output" ]]; then
echo "Backport debug: response body:"
cat "$output"
fi
return 1
fi
echo "Backport debug: ${method} ${path} -> HTTP ${status}"
if [[ ! "$status" =~ ^2 ]]; then
echo "Backport debug: response body:"
cat "$output"
return 1
fi
}
echo "Backport debug: event=${GITHUB_EVENT_NAME:-unknown}"
echo "Backport debug: server=${GITHUB_SERVER_URL}"
echo "Backport debug: api=${api_url}"
echo "Backport debug: repository=${GITHUB_REPOSITORY}"
echo "Backport debug: pr=${PR_NUMBER}"
echo "Backport debug: actor=${GITHUB_ACTOR:-unknown}"
git config user.name "SteamWar Backport Bot"
git config user.email "actions@steamwar.de"
if [[ "${GITHUB_SERVER_URL}" == https://* ]]; then
auth_host="${GITHUB_SERVER_URL#https://}"
git remote set-url origin "https://oauth2:${GITHUB_TOKEN}@${auth_host}/${GITHUB_REPOSITORY}.git"
fi
git fetch --prune origin '+refs/heads/version/*:refs/remotes/origin/version/*'
api_request GET "${repo_api_path}" repo-debug.json
jq -r '"Backport debug: repo permissions admin=\(.permissions.admin // "unknown") push=\(.permissions.push // "unknown") pull=\(.permissions.pull // "unknown")"' repo-debug.json || true
echo "Backport debug: GET ${repo_api_path}/pulls/${PR_NUMBER}.diff"
curl -fsSL -w "Backport debug: GET ${repo_api_path}/pulls/${PR_NUMBER}.diff -> HTTP %{http_code}\n" \
-H "Accept: text/plain" \
-H "Authorization: token ${GITHUB_TOKEN}" \
"${api_url}${repo_api_path}/pulls/${PR_NUMBER}.diff" \
-o pull-request.diff
if ! grep -Eq "^diff --git a/${BACKPORT_PATH}/" pull-request.diff; then
echo "Pull request #${PR_NUMBER} has no ${BACKPORT_PATH} changes to backport."
exit 0
fi
mapfile -t target_branches < <(git for-each-ref --format='%(refname:strip=3)' refs/remotes/origin/version)
if [[ "${#target_branches[@]}" -eq 0 ]]; then
echo "No version/* branches found."
exit 0
fi
for target_branch in "${target_branches[@]}"; do
safe_target="${target_branch//\//-}"
backport_branch="${BACKPORT_BRANCH_PREFIX}/pr-${PR_NUMBER}-to-${safe_target}"
git checkout -B "${backport_branch}" "origin/${target_branch}"
git reset --hard "origin/${target_branch}"
if ! git apply --3way --index --include="${BACKPORT_PATH}/**" pull-request.diff; then
echo "Failed to apply CommonCore backport for ${target_branch}."
exit 1
fi
if git diff --cached --quiet; then
echo "CommonCore changes from #${PR_NUMBER} are already present in ${target_branch}."
continue
fi
git commit -m "Backport CommonCore changes from #${PR_NUMBER}" -m "${PR_TITLE}"
git push --force-with-lease origin "${backport_branch}"
api_request GET "${repo_api_path}/pulls?state=open" open-pulls.json
open_pr_number="$(jq -r --arg base "$target_branch" --arg head "$backport_branch" '[.[] | select(.base.ref == $base and .head.ref == $head) | (.number // .index)][0] // empty' open-pulls.json)"
if [[ -n "$open_pr_number" ]]; then
echo "Backport PR #${open_pr_number} already exists for ${target_branch}."
continue
fi
pr_body="$(printf 'Automatic CommonCore backport of #%s.\n\nOriginal PR title: %s\n\nOnly files below `CommonCore/` are included.' "$PR_NUMBER" "$PR_TITLE")"
jq -n \
--arg base "$target_branch" \
--arg head "$backport_branch" \
--arg title "Backport CommonCore changes from #${PR_NUMBER} to ${target_branch}" \
--arg body "$pr_body" \
'{base: $base, head: $head, title: $title, body: $body}' > create-pull.json
echo "Backport debug: create PR base=${target_branch} head=${backport_branch}"
api_request POST "${repo_api_path}/pulls" create-pull-response.json create-pull.json
done
+145
View File
@@ -0,0 +1,145 @@
name: Deploy
on:
push:
branches:
- main
- version/*
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v6
- name: Setup Java 8
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 8
- name: Setup Java 11
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 11
- name: Setup Java 17
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 17
- name: Setup Java
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
- name: Setup Maven Repository
env:
SW_MAVEN_CREDENTIALS: ${{ secrets.SW_MAVEN_CREDENTIALS }}
run: |
echo "$SW_MAVEN_CREDENTIALS" > steamwar.properties
- name: Build with Gradle
run: ./gradlew build --no-daemon
- name: Stage deploy artifacts
shell: bash
run: |
set -euo pipefail
rm -rf deploy
mkdir -p deploy
cp "BauSystem/build/libs/BauSystem-all.jar" "deploy/BauSystem.jar"
cp "LegacyBauSystem/build/libs/LegacyBauSystem.jar" "deploy/BauSystem-1.12.jar"
cp "FightSystem/build/libs/FightSystem-all.jar" "deploy/FightSystem.jar"
cp "KotlinCore/build/libs/KotlinCore-all.jar" "deploy/KotlinCore.jar"
cp "TNTLeague/build/libs/TNTLeague.jar" "deploy/TNTLeague.jar"
cp "LobbySystem/build/libs/LobbySystem.jar" "deploy/LobbySystem.jar"
cp "MissileWars/build/libs/MissileWars.jar" "deploy/MissileWars.jar"
cp "Realtime/build/libs/Realtime.jar" "deploy/RealTime.jar"
cp "SchematicSystem/build/libs/SchematicSystem-all.jar" "deploy/SchematicSystem.jar"
cp "SpigotCore/build/libs/SpigotCore-all.jar" "deploy/SpigotCore.jar"
cp "Teamserver/build/libs/Teamserver.jar" "deploy/Builder.jar"
cp "TowerRun/build/libs/TowerRun.jar" "deploy/TowerRun.jar"
cp "VelocityCore/Persistent/build/libs/Persistent.jar" "deploy/PersistentVelocityCore.jar"
cp "VelocityCore/Dependencies/build/libs/Dependencies-all.jar" "deploy/DependenciesVelocityCore.jar"
cp "VelocityCore/build/libs/VelocityCore-all.jar" "deploy/VelocityCore.jar"
cp "WebsiteBackend/build/libs/WebsiteBackend-all.jar" "deploy/website-api.jar"
cp "CLI/build/distributions/sw.zip" "deploy/sw.zip"
- name: Upload deploy artifacts
uses: actions/upload-artifact@v3
with:
name: steamwar-jars
path: deploy/
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: build
steps:
- name: Download deploy artifacts
uses: actions/download-artifact@v3
with:
name: steamwar-jars
path: deploy
- name: Resolve deploy target
id: target
shell: bash
run: |
set -euo pipefail
if [[ "${GITHUB_REF_NAME}" == "main" ]]; then
echo "path=/jars/current" >> "$GITHUB_OUTPUT"
elif [[ "${GITHUB_REF_NAME}" == version/* ]]; then
version="${GITHUB_REF_NAME#version/}"
if [[ ! "$version" =~ ^[A-Za-z0-9._-]+$ ]]; then
echo "Unsupported version branch name: ${GITHUB_REF_NAME}" >&2
exit 1
fi
echo "path=/jars/${version}" >> "$GITHUB_OUTPUT"
else
echo "Unsupported deployment branch: ${GITHUB_REF_NAME}" >&2
exit 1
fi
- name: Upload jars with scp
shell: bash
env:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT }}
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
DEPLOY_PATH: ${{ steps.target.outputs.path }}
run: |
set -euo pipefail
: "${DEPLOY_HOST:?Missing DEPLOY_HOST secret}"
: "${DEPLOY_USER:?Missing DEPLOY_USER secret}"
: "${DEPLOY_SSH_KEY:?Missing DEPLOY_SSH_KEY secret}"
port="${DEPLOY_PORT:-22}"
mkdir -p ~/.ssh
chmod 700 ~/.ssh
echo "$DEPLOY_SSH_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
ssh-keyscan -p "$port" "$DEPLOY_HOST" >> ~/.ssh/known_hosts
ssh -i ~/.ssh/deploy_key -p "$port" "${DEPLOY_USER}@${DEPLOY_HOST}" "mkdir -p '$DEPLOY_PATH'"
scp -i ~/.ssh/deploy_key -P "$port" deploy/* "${DEPLOY_USER}@${DEPLOY_HOST}:$DEPLOY_PATH/"
- name: Restart Services
shell: bash
env:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
DEPLOY_PORT: ${{ secrets.DEPLOY_PORT }}
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: |
set -euo pipefail
ssh -i ~/.ssh/deploy_key -p "$DEPLOY_PORT" "${DEPLOY_USER}@${DEPLOY_HOST}" "sudo systemctl restart api.service"
ssh -i ~/.ssh/deploy_key -p "$DEPLOY_PORT" "${DEPLOY_USER}@${DEPLOY_HOST}" "unzip -o /jars/current/sw.zip -d /jars"
+76
View File
@@ -0,0 +1,76 @@
name: Pull Request Build
on:
pull_request:
permissions:
contents: write
pull-requests: write
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@v6
- name: Setup Java 8
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 8
- name: Setup Java 11
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 11
- name: Setup Java 17
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 17
- name: Setup Java
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6
- name: Setup Maven Repository
env:
SW_MAVEN_CREDENTIALS: ${{ secrets.SW_MAVEN_CREDENTIALS }}
run: |
echo "$SW_MAVEN_CREDENTIALS" > steamwar.properties
- name: Build with Gradle
run: ./gradlew build --no-daemon
- name: Merge successful backport PR
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.CI_BOT_TOKEN }}
BACKPORT_BRANCH_PREFIX: backport/commoncore
run: |
set -euo pipefail
head_branch="$(jq -r '.pull_request.head.ref // ""' "$GITHUB_EVENT_PATH")"
base_branch="$(jq -r '.pull_request.base.ref // ""' "$GITHUB_EVENT_PATH")"
pr_number="$(jq -r '.pull_request.number' "$GITHUB_EVENT_PATH")"
if [[ "${head_branch}" != "${BACKPORT_BRANCH_PREFIX}/"* ]]; then
echo "Not a CommonCore backport PR."
exit 0
fi
if [[ "${base_branch}" != version/* ]]; then
echo "Backport PR target is not a version/* branch."
exit 0
fi
api_url="${GITHUB_API_URL:-${GITHUB_SERVER_URL}/api/v1}"
curl -fsS -X POST \
-H "Accept: application/json" \
-H "Authorization: token ${GITHUB_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"Do":"merge","delete_branch_after_merge":true}' \
"${api_url}/repos/${GITHUB_REPOSITORY}/pulls/${pr_number}/merge"
+1 -1
View File
@@ -21,4 +21,4 @@ lib
/WebsiteBackend/logs
/WebsiteBackend/skins
/WebsiteBackend/config.json
/WebsiteBackend/sessions
/WebsiteBackend/sessions
-35
View File
@@ -1,35 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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/>.
*/
plugins {
steamwar.java
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
compileOnly(project(":BauSystem:BauSystem_Main", "default"))
compileOnly(project(":SpigotCore", "default"))
compileOnly(libs.nms15)
compileOnly(libs.worldedit15)
}
@@ -1,239 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.World;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockTypes;
import de.steamwar.bausystem.region.Point;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Waterlogged;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiPredicate;
import java.util.logging.Level;
public class FlatteningWrapper15 implements FlatteningWrapper {
@Override
public boolean isNoBook(ItemStack item) {
return item.getType() != Material.WRITABLE_BOOK && item.getType() != Material.WRITTEN_BOOK;
}
private static final Set<Material> unpushable = new HashSet<>(Arrays.asList(Material.BARRIER, Material.BEACON, Material.COMMAND_BLOCK, Material.CHAIN_COMMAND_BLOCK, Material.REPEATING_COMMAND_BLOCK, Material.ENCHANTING_TABLE, Material.END_GATEWAY, Material.END_PORTAL, Material.ENDER_CHEST, Material.GRINDSTONE, Material.JIGSAW, Material.JUKEBOX, Material.NETHER_PORTAL, Material.OBSIDIAN, Material.STRUCTURE_VOID, Material.BARREL, Material.BEEHIVE, Material.BEE_NEST, Material.BLAST_FURNACE, Material.BREWING_STAND, Material.CHEST, Material.DAYLIGHT_DETECTOR, Material.DISPENSER, Material.DROPPER, Material.FURNACE, Material.HOPPER, Material.LECTERN, Material.SMOKER, Material.TRAPPED_CHEST));
// TODO: FLOWER
private static final Set<Material> breaking = new HashSet<>(Arrays.asList(Material.BAMBOO, Material.CACTUS, Material.CAKE, Material.CARVED_PUMPKIN, Material.CHORUS_FLOWER, Material.CHORUS_PLANT, Material.COBWEB, Material.COCOA, Material.DRAGON_EGG, Material.FIRE, Material.FLOWER_POT, Material.JACK_O_LANTERN, Material.LADDER, Material.LAVA, Material.LAVA, Material.LEVER, Material.LILY_PAD, Material.MELON, Material.NETHER_WART, Material.PUMPKIN, Material.COMPARATOR, Material.REDSTONE_WIRE, Material.REPEATER, Material.TORCH, Material.STRUCTURE_VOID, Material.SCAFFOLDING, Material.SEA_PICKLE, Material.SNOW, Material.SUGAR_CANE, Material.TORCH, Material.TRIPWIRE, Material.TRIPWIRE_HOOK, Material.TURTLE_EGG, Material.VINE, Material.WATER, Material.WHEAT));
@Override
public boolean isUnpusheable(Material material) {
if (unpushable.contains(material)) {
return true;
}
String name = material.name();
return name.contains("BANNER") || name.contains("SIGN");
}
@Override
public boolean isBreakingOnPush(Material material) {
if (breaking.contains(material)) {
return true;
}
String name = material.name();
return name.contains("BED") || name.contains("BUTTON") || name.contains("CARPET") || (name.contains("DOOR") && !name.contains("TRAPDOOR")) || name.contains("HEAD") || name.contains("LEAVES") || name.contains("MUSHROOM") || name.contains("PRESSURE_PLATE") || name.contains("SHULKER_BOX");
}
@Override
public boolean isWorldEditCommand(String command) {
if (command.startsWith("/")) {
command = command.replaceFirst("/", "");
}
command = command.toLowerCase();
return WorldEdit.getInstance().getPlatformManager().getPlatformCommandManager().getCommandManager().containsCommand(command);
}
private static final WorldEditPlugin WORLDEDIT_PLUGIN = Objects.requireNonNull((WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit"));
private static final World BUKKITWORLD = new BukkitWorld(Bukkit.getWorlds().get(0));
@Override
public void setSelection(Player p, Point minPoint, Point maxPoint) {
WORLDEDIT_PLUGIN.getSession(p).setRegionSelector(BUKKITWORLD, new CuboidRegionSelector(BUKKITWORLD, minPoint.toBlockVector3(), maxPoint.toBlockVector3()));
}
@Override
public Clipboard loadSchematic(File file) {
Clipboard clipboard;
try (ClipboardReader reader = Objects.requireNonNull(ClipboardFormats.findByFile(file)).getReader(new FileInputStream(file))) {
clipboard = reader.read();
} catch (NullPointerException | IOException e) {
throw new SecurityException("Bausystem schematic not found", e);
}
return clipboard;
}
@Override
public EditSession paste(PasteBuilder pasteBuilder) {
try (EditSession e = WorldEdit.getInstance().getEditSessionFactory().getEditSession(new BukkitWorld(Bukkit.getWorlds().get(0)), -1)) {
Clipboard clipboard = pasteBuilder.getClipboard();
if (!pasteBuilder.getMappers().isEmpty()) {
BlockVector3 minimum = clipboard.getRegion().getMinimumPoint();
for (int x = 0; x < clipboard.getDimensions().getX(); x++) {
for (int y = 0; y < clipboard.getDimensions().getY(); y++) {
for (int z = 0; z < clipboard.getDimensions().getZ(); z++) {
BlockVector3 pos = minimum.add(x, y, z);
pasteBuilder.getMappers().forEach(mapper -> mapper.accept(clipboard, pos));
}
}
}
}
AtomicReference<BlockVector3> pastePoint = new AtomicReference<>();
if (!pasteBuilder.getPredicates().isEmpty()) {
e.setMask(new Mask() {
@Override
public boolean test(BlockVector3 blockVector3) {
BaseBlock block = clipboard.getFullBlock(blockVector3.subtract(pastePoint.get()).add(clipboard.getRegion().getMinimumPoint()));
String blockName = block.getBlockType().toString().toLowerCase();
for (BiPredicate<BaseBlock, String> predicate : pasteBuilder.getPredicates()) {
if (!predicate.test(block, blockName)) return false;
}
return true;
}
public Mask copy() {
return this;
}
@Nullable
@Override
public Mask2D toMask2D() {
return null;
}
});
}
ClipboardHolder ch = new ClipboardHolder(clipboard);
BlockVector3 dimensions = clipboard.getDimensions();
BlockVector3 v = BlockVector3.at(pasteBuilder.getPastPoint().getX(), pasteBuilder.getPastPoint().getY(), pasteBuilder.getPastPoint().getZ());
BlockVector3 offset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin());
if (pasteBuilder.isRotate()) {
ch.setTransform(new AffineTransform().rotateY(180));
v = v.add(dimensions.getX() / 2, 0, dimensions.getZ() / 2).subtract(offset.multiply(-1, 1, -1)).subtract(0, 0, 1);
} else {
v = v.subtract(dimensions.getX() / 2, 0, dimensions.getZ() / 2).subtract(offset);
}
pastePoint.set(v);
if (pasteBuilder.isReset()) {
e.setBlocks(new CuboidRegion(pasteBuilder.getMinPoint().toBlockVector3(), pasteBuilder.getMaxPoint().toBlockVector3()), Objects.requireNonNull(BlockTypes.AIR).getDefaultState().toBaseBlock());
if (pasteBuilder.getWaterLevel() != 0) {
e.setBlocks(new CuboidRegion(pasteBuilder.getMinPoint().toBlockVector3(), pasteBuilder.getMaxPoint().toBlockVector3().withY(pasteBuilder.getWaterLevel())), Objects.requireNonNull(BlockTypes.WATER).getDefaultState().toBaseBlock());
}
}
Operations.completeBlindly(ch.createPaste(e).to(v).ignoreAirBlocks(pasteBuilder.isIgnoreAir()).build());
return e;
} catch (WorldEditException e) {
throw new SecurityException(e.getMessage(), e);
}
}
@Override
public Clipboard copy(Point minPoint, Point maxPoint, Point copyPoint) {
BukkitWorld bukkitWorld = new BukkitWorld(Bukkit.getWorlds().get(0));
CuboidRegion region = new CuboidRegion(bukkitWorld, minPoint.toBlockVector3(), maxPoint.toBlockVector3());
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
try (EditSession e = WorldEdit.getInstance().getEditSessionFactory().getEditSession(bukkitWorld, -1)) {
ForwardExtentCopy copy = new ForwardExtentCopy(
e, region, clipboard, region.getMinimumPoint()
);
copy.setCopyingEntities(false);
copy.setCopyingBiomes(false);
Operations.complete(copy);
clipboard.setOrigin(copyPoint.toBlockVector3());
return clipboard;
} catch (WorldEditException e) {
Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
return null;
}
}
@Override
public boolean backup(Point minPoint, Point maxPoint, File file) {
Clipboard clipboard = copy(minPoint, maxPoint, minPoint);
try (ClipboardWriter writer = BuiltInClipboardFormat.SPONGE_SCHEMATIC.getWriter(new FileOutputStream(file))) {
writer.write(clipboard);
return true;
} catch (IOException e) {
Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
return false;
}
}
@Override
public boolean inWater(org.bukkit.World world, Vector tntPosition) {
Block block = world.getBlockAt(tntPosition.getBlockX(), tntPosition.getBlockY(), tntPosition.getBlockZ());
if (block.getType() == Material.WATER)
return true;
BlockData data = block.getBlockData();
if (!(data instanceof Waterlogged))
return false;
return ((Waterlogged) data).isWaterlogged();
}
}
@@ -1,129 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import de.steamwar.Reflection;
import de.steamwar.bausystem.features.util.NoClipCommand;
import net.minecraft.server.v1_15_R1.*;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_15_R1.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.List;
public class NMSWrapper15 implements NMSWrapper {
private static final Reflection.Field<EnumGamemode> playerGameMode = Reflection.getField(PlayerInteractManager.class, EnumGamemode.class, 0);
@Override
@SuppressWarnings("deprecation")
public void setInternalGameMode(Player player, GameMode gameMode) {
playerGameMode.set(((CraftPlayer) player).getHandle().playerInteractManager, EnumGamemode.getById(gameMode.getValue()));
}
@Override
public void setSlotToItemStack(Player player, Object o) {
PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) o;
int index = packetPlayInSetCreativeSlot.b();
if (index >= 36 && index <= 44) {
index -= 36;
} else if (index > 44) {
index -= 5;
} else if (index <= 8) {
index = index - 8 + 36;
}
player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.getItemStack()));
if (index < 9) player.getInventory().setHeldItemSlot(index);
player.updateInventory();
}
private static final Reflection.Field<Integer> gameStateChangeReason = Reflection.getField(NoClipCommand.gameStateChange, int.class, 0);
@Override
public void setGameStateChangeReason(Object packet) {
gameStateChangeReason.set(packet, 3);
}
@Override
public void setPlayerBuildAbilities(Player player) {
((CraftPlayer) player).getHandle().abilities.mayBuild = true;
((CraftPlayer) player).getHandle().abilities.canInstantlyBuild = true;
}
@Override
public Material pathMaterial() {
return Material.GRASS_PATH;
}
private static final int threshold = 2048;
@Override
public boolean checkItemStack(ItemStack item) {
net.minecraft.server.v1_15_R1.ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
NBTTagCompound tag = nmsItem.getTag();
if (tag != null && tag.hasKey("BlockEntityTag")) {
NBTTagCompound blockTag = tag.getCompound("BlockEntityTag");
if (blockTag.hasKey("Items")) {
return drillDown(blockTag.getList("Items", 10), 0, 0) > threshold;
}
}
return false;
}
private int drillDown(NBTTagList items, int layer, int start) {
if (layer > 2) return start + threshold;
int invalid = start;
for (NBTBase nbtBase : items) {
if (!(nbtBase instanceof NBTTagCompound))
continue;
NBTTagCompound slot = (NBTTagCompound) nbtBase;
if (slot.hasKey("tag")) {
invalid += slot.getByte("Count");
NBTTagCompound iTag = slot.getCompound("tag");
if (iTag.hasKey("BlockEntityTag")) {
NBTTagCompound blockTag = iTag.getCompound("BlockEntityTag");
if (blockTag.hasKey("Items")) {
invalid = drillDown(blockTag.getList("Items", 10), layer + 1, invalid);
}
}
}
if (invalid > threshold)
break;
}
return invalid;
}
private final Class<?> explosionPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundExplodePacket");
private final Reflection.Field<Double> a = Reflection.getField(explosionPacket, double.class, 0);
private final Reflection.Field<Double> b = Reflection.getField(explosionPacket, double.class, 1);
private final Reflection.Field<Double> c = Reflection.getField(explosionPacket, double.class, 2);
private final Reflection.Field<Float> d = Reflection.getField(explosionPacket, float.class, 0);
private final Reflection.Field<List> e = Reflection.getField(explosionPacket, List.class, 0);
@Override
public Object resetExplosionKnockback(Object packet) {
PacketPlayOutExplosion packetPlayOutExplosion = (PacketPlayOutExplosion) packet;
return new PacketPlayOutExplosion(a.get(packetPlayOutExplosion), b.get(packetPlayOutExplosion), c.get(packetPlayOutExplosion), d.get(packetPlayOutExplosion), e.get(packetPlayOutExplosion), Vec3D.a);
}
}
@@ -1,43 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import org.bukkit.Material;
public class PlaceItemWrapper15 implements PlaceItemWrapper {
public PlaceItemWrapper15() {
for (Material material : Material.values()) {
if (!material.isBlock()) continue;
if (material.isLegacy()) continue;
String nonWall = material.name().replace("_WALL_", "").replace("WALL_", "").replace("_WALL", "");
try {
Material nonWallMaterial = Material.valueOf(nonWall);
if (nonWallMaterial != material && nonWallMaterial.isItem() && !nonWallMaterial.isBlock()) {
BLOCK_MATERIAL_TO_WALL_BLOCK_MATERIAL.put(nonWallMaterial, material);
}
} catch (Exception e) {
// Ignore
}
}
ITEM_MATERIAL_TO_BLOCK_MATERIAL.put(Material.REDSTONE, Material.REDSTONE_WIRE);
}
}
@@ -1,51 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import de.steamwar.Reflection;
import net.minecraft.server.v1_15_R1.EntityPlayer;
import net.minecraft.server.v1_15_R1.PacketPlayInFlying;
import org.bukkit.craftbukkit.v1_15_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
public class PlayerMovementWrapper15 implements PlayerMovementWrapper {
@Override
public void setPosition(Player player, Object object) {
PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle();
if (Float.isNaN(packetPlayInFlying.a(Float.NaN))) {
entityPlayer.e(packetPlayInFlying.a(0.0), packetPlayInFlying.b(0.0), packetPlayInFlying.c(0.0));
} else {
entityPlayer.setLocation(packetPlayInFlying.a(0.0), packetPlayInFlying.b(0.0), packetPlayInFlying.c(0.0), packetPlayInFlying.a(0F), packetPlayInFlying.b(0F));
}
}
@Override
public Object convertToOut(Player player, Object object) {
PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
Object packet = Reflection.newInstance(teleportPacket);
teleportEntity.set(packet, player.getEntityId());
teleportPosition.set(packet, packetPlayInFlying.a(0.0), packetPlayInFlying.b(0.0), packetPlayInFlying.c(0.0),
Float.isNaN(packetPlayInFlying.a(Float.NaN)) ? player.getLocation().getYaw() : packetPlayInFlying.a(0.0F),
Float.isNaN(packetPlayInFlying.b(Float.NaN)) ? player.getLocation().getPitch() : packetPlayInFlying.b(0.0F));
return packet;
}
}
@@ -1,23 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
public class TickListener15 implements TickListener {
}
@@ -1,140 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.bossbar.BossBarService;
import de.steamwar.bausystem.utils.tps.TPSFreezeUtils;
import de.steamwar.bausystem.utils.tps.TPSLimitUtils;
import de.steamwar.core.TPSWarpUtils;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class TickManager15 implements TickManager, Listener {
private static float currentTPSLimit = 20;
private boolean currentlyStepping = false;
private float currentLimit;
private int stepsTotal;
private int stepsLeft;
@Override
public boolean canFreeze() {
return TPSFreezeUtils.isCanFreeze();
}
@Override
public void setTickRate(float tickRate) {
if (currentlyStepping) {
currentlyStepping = false;
Bukkit.getOnlinePlayers().forEach(player -> {
BossBarService.instance.remove(player, Region.getGlobalRegion(), "TickStep");
});
}
TPSWarpUtils.warp(tickRate);
if (currentTPSLimit == 0 && tickRate != 0) {
TPSFreezeUtils.unfreeze();
}
currentTPSLimit = tickRate;
if (tickRate == 0) {
TPSLimitUtils.unlimit();
TPSFreezeUtils.freeze();
} else if (tickRate < 20.0) {
TPSLimitUtils.limit(tickRate);
} else if (tickRate >= 20) {
TPSLimitUtils.unlimit();
}
}
@Override
public boolean isFrozen() {
return TPSFreezeUtils.frozen();
}
@Override
public void setFreeze(boolean freeze) {
if (freeze) {
setTickRate(0);
}
}
@Override
public void stepTicks(int ticks) {
currentLimit = 0;
setTickRate(20);
stepsLeft = ticks;
stepsTotal = ticks;
currentlyStepping = true;
}
@Override
public void sprintTicks(int ticks) {
currentLimit = currentTPSLimit;
setTickRate(4000);
stepsLeft = ticks;
stepsTotal = ticks;
currentlyStepping = true;
}
@Override
public boolean isSprinting() {
return currentlyStepping && currentTPSLimit > 20;
}
@Override
public boolean isStepping() {
return currentlyStepping && currentTPSLimit <= 20;
}
@Override
public float getTickRate() {
return currentTPSLimit;
}
@Override
public void setBlockTpsPacket(boolean block) {
}
@Override
public long getTotalTicks() {
return stepsTotal;
}
@Override
public long getDoneTicks() {
return stepsTotal - stepsLeft;
}
@Override
public long getRemainingTicks() {
return stepsLeft;
}
@EventHandler
public void onTickEnd(TickEndEvent event) {
if (!currentlyStepping) return;
stepsLeft--;
if (stepsLeft <= 0) {
setTickRate(currentLimit);
}
}
}
@@ -1,131 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils.tps;
import de.steamwar.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.core.BountifulWrapper;
import de.steamwar.core.ChatWrapper;
import de.steamwar.core.Core;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@UtilityClass
class PacketCache {
private static List<Object> packets = new ArrayList<>();
private static Set<Entity> entities = new HashSet<>();
private static BukkitTask task = null;
private static Class<?> vec3dClass = Reflection.getClass("net.minecraft.world.phys.Vec3");
private static Reflection.Field<Object> zeroVec3d = (Reflection.Field<Object>) Reflection.getField(vec3dClass, vec3dClass, 0);
private static Object ZERO_VEC3D = zeroVec3d.get(null);
private static Class<?> velocityPacketClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket");
private static Reflection.Constructor velocityPacketConstructor = Reflection.getConstructor(velocityPacketClass, int.class, vec3dClass);
private static Class<?> teleportPacketClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket");
private static Class<?> entityClass = Reflection.getClass("net.minecraft.world.entity.Entity");
private static Reflection.Constructor teleportPacketConstructor = Reflection.getConstructor(teleportPacketClass, entityClass);
private static Class<?> craftEntityClass = Reflection.getClass("org.bukkit.craftbukkit.entity.CraftEntity");
private static Reflection.Method getHandle = Reflection.getMethod(craftEntityClass, "getHandle");
private static Object noGravityDataWatcher = BountifulWrapper.impl.getDataWatcherObject(5, Boolean.class);
private static Object fuseDataWatcher = BountifulWrapper.impl.getDataWatcherObject(8, Integer.class);
public void continuousSendCache() {
if (task != null) {
return;
}
createPackets();
task = new BukkitRunnable() {
@Override
public void run() {
_sendCache();
}
}.runTaskTimer(Core.getInstance(), 1, 1);
}
public void sendCache() {
if (task != null) {
task.cancel();
task = null;
}
_sendCache();
}
private void _sendCache() {
createPackets();
for (Player player : Bukkit.getOnlinePlayers()) {
for (Object packet : packets) {
TinyProtocol.instance.sendPacket(player, packet);
}
}
}
public void clearCache() {
packets.clear();
entities.clear();
if (task != null) {
task.cancel();
task = null;
}
}
private void createPackets() {
if (entities.stream().anyMatch(Entity::isDead)) {
entities.clear();
packets.clear();
}
List<Entity> entities = Bukkit.getWorlds().get(0).getEntities().stream()
.filter(e -> !(e instanceof Player))
.filter(e -> PacketCache.entities.add(e))
.collect(Collectors.toList());
for (Entity entity : entities) {
packets.add(teleportPacketConstructor.invoke(getHandle.invoke(entity)));
}
for (Entity entity : entities) {
packets.add(velocityPacketConstructor.invoke(entity.getEntityId(), ZERO_VEC3D));
}
for (Entity entity : entities) {
packets.add(ChatWrapper.impl.getDataWatcherPacket(entity.getEntityId(), noGravityDataWatcher, true));
}
for (Entity entity : entities) {
if (!(entity instanceof TNTPrimed)) continue;
TNTPrimed tnt = (TNTPrimed) entity;
int fuse = tnt.getFuseTicks();
packets.add(ChatWrapper.impl.getDataWatcherPacket(entity.getEntityId(), fuseDataWatcher, fuse - (fuse % 5) + 1));
}
}
}
@@ -1,76 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils.tps;
import de.steamwar.Reflection;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.World;
@UtilityClass
public class TPSFreezeUtils {
private static Reflection.Field<Boolean> fieldAccessor;
@Getter
private static final boolean canFreeze;
private static final Reflection.Method getWorldHandle = Reflection.getTypedMethod(Reflection.getClass("org.bukkit.craftbukkit.CraftWorld"), "getHandle", null);
@Getter
private static boolean frozen = false;
private static final World world = Bukkit.getWorlds().get(0);
static {
Reflection.Field<Boolean> fieldAccessor;
try {
fieldAccessor = Reflection.getField(Reflection.getClass("net.minecraft.server.level.ServerLevel"), "freezed", boolean.class);
} catch (IllegalArgumentException e) {
fieldAccessor = null;
}
canFreeze = fieldAccessor != null;
TPSFreezeUtils.fieldAccessor = fieldAccessor;
}
public void freeze() {
setFreeze(world, true);
}
public void unfreeze() {
setFreeze(world, false);
}
public boolean frozen() {
return canFreeze && frozen;
}
private void setFreeze(World world, boolean state) {
if (canFreeze) {
fieldAccessor.set(getWorldHandle.invoke(world), state);
if (state) {
PacketCache.continuousSendCache();
} else {
PacketCache.clearCache();
}
frozen = state;
}
}
}
@@ -1,124 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils.tps;
import de.steamwar.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.bausystem.utils.PlayerMovementWrapper;
import de.steamwar.core.Core;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.BiFunction;
@UtilityClass
public class TPSLimitUtils {
private static long currentTime = System.nanoTime();
private static BukkitTask tpsLimiter = null;
private static Queue<Runnable> packetQueue = new ConcurrentLinkedQueue<>();
public void unlimit() {
if (tpsLimiter != null) tpsLimiter.cancel();
tpsLimiter = null;
}
public void limit(double tps) {
if (tpsLimiter != null) tpsLimiter.cancel();
double delay = 20 / tps;
int loops = (int) Math.ceil(delay);
long sleepDelay = (long) (50 * delay) / loops;
tpsLimiter = Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> {
PacketCache.sendCache();
for (int i = 0; i < loops; i++) {
sleepUntilNextTick(sleepDelay);
PacketCache.sendCache();
while (true) {
Runnable runnable = packetQueue.poll();
if (runnable == null) break;
runnable.run();
}
}
PacketCache.clearCache();
}, 0, 1);
}
private void sleepUntilNextTick(long neededDelta) {
long lastTime = currentTime;
currentTime = System.nanoTime();
long timeDelta = (currentTime - lastTime) / 1000000;
if (neededDelta - timeDelta < 0) return;
try {
Thread.sleep(neededDelta - timeDelta);
currentTime = System.nanoTime();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
/*
static {
long timeInterval = 50;
final long[] lastTime = {System.currentTimeMillis()};
final double[] tps = {20.0};
Bukkit.getScheduler().runTaskTimer(Core.getInstance(), () -> {
long currentTime = System.currentTimeMillis();
if (currentTime > lastTime[0]) {
tps[0] = (double)timeInterval / (double)(currentTime - lastTime[0]) * 20.0;
}
lastTime[0] = currentTime;
Bukkit.getOnlinePlayers().forEach(player -> {
SWUtils.sendToActionbar(player, String.valueOf((int) (tps[0] * 10.0) / 10.0));
});
}, timeInterval / 50L, timeInterval / 50L);
}
*/
private static final Class<?> position = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos");
private static final Class<?> positionLook = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot");
static {
BiFunction<Player, Object, Object> positionSetter = (player, o) -> {
if (tpsLimiter != null) {
Object object = PlayerMovementWrapper.impl.convertToOut(player, o);
packetQueue.add(() -> {
PlayerMovementWrapper.impl.setPosition(player, o);
Bukkit.getOnlinePlayers().forEach(p -> {
if (p == player) return;
TinyProtocol.instance.sendPacket(p, object);
});
});
return null;
}
return o;
};
TinyProtocol.instance.addFilter(position, positionSetter);
TinyProtocol.instance.addFilter(positionLook, positionSetter);
}
}
-35
View File
@@ -1,35 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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/>.
*/
plugins {
steamwar.java
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
compileOnly(project(":BauSystem:BauSystem_Main", "default"))
compileOnly(project(":SpigotCore", "default"))
compileOnly(libs.spigotapi)
compileOnly(libs.nms18)
}
@@ -1,135 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import de.steamwar.Reflection;
import de.steamwar.bausystem.features.util.NoClipCommand;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.protocol.game.*;
import net.minecraft.server.level.PlayerInteractManager;
import net.minecraft.world.level.EnumGamemode;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.List;
public class NMSWrapper18 implements NMSWrapper {
private static final Reflection.Field<EnumGamemode> playerGameMode = Reflection.getField(PlayerInteractManager.class, EnumGamemode.class, 0);
@Override
@SuppressWarnings("deprecation")
public void setInternalGameMode(Player player, GameMode gameMode) {
playerGameMode.set(((CraftPlayer) player).getHandle().d, EnumGamemode.a(gameMode.getValue()));
}
@Override
public void setSlotToItemStack(Player player, Object o) {
PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) o;
int index = packetPlayInSetCreativeSlot.b();
if (index >= 36 && index <= 44) {
index -= 36;
} else if (index > 44) {
index -= 5;
} else if (index <= 8) {
index = index - 8 + 36;
}
player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.c()));
if (index < 9) player.getInventory().setHeldItemSlot(index);
player.updateInventory();
}
private static final Reflection.Field<PacketPlayOutGameStateChange.a> gameStateChangeReason = Reflection.getField(NoClipCommand.gameStateChange, PacketPlayOutGameStateChange.a.class, 12);
@Override
public void setGameStateChangeReason(Object packet) {
gameStateChangeReason.set(packet, PacketPlayOutGameStateChange.d);
}
@Override
public void setPlayerBuildAbilities(Player player) {
((CraftPlayer) player).getHandle().fs().d = true;
((CraftPlayer) player).getHandle().fs().e = true;
}
@Override
public Material pathMaterial() {
return Material.DIRT_PATH;
}
private static final int threshold = 2048;
@Override
public boolean checkItemStack(ItemStack item) {
net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
NBTTagCompound tag = nmsItem.t();
if (tag != null && tag.e("BlockEntityTag")) {
NBTTagCompound blockTag = tag.p("BlockEntityTag");
if (blockTag.e("Items")) {
return drillDown(blockTag.c("Items", 10), 0, 0) > threshold;
}
}
return false;
}
private int drillDown(NBTTagList items, int layer, int start) {
if (layer > 2) return start + threshold;
int invalid = start;
for (NBTBase nbtBase : items) {
if (!(nbtBase instanceof NBTTagCompound))
continue;
NBTTagCompound slot = (NBTTagCompound) nbtBase;
if (slot.e("tag")) {
invalid += slot.f("Count");
NBTTagCompound iTag = slot.p("tag");
if (iTag.e("BlockEntityTag")) {
NBTTagCompound blockTag = iTag.p("BlockEntityTag");
if (blockTag.e("Items")) {
invalid = drillDown(blockTag.c("Items", 10), layer + 1, invalid);
}
}
}
if (invalid > threshold)
break;
}
return invalid;
}
private final Class<?> explosionPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundExplodePacket");
private final Reflection.Field<Double> a = Reflection.getField(explosionPacket, double.class, 0);
private final Reflection.Field<Double> b = Reflection.getField(explosionPacket, double.class, 1);
private final Reflection.Field<Double> c = Reflection.getField(explosionPacket, double.class, 2);
private final Reflection.Field<Float> d = Reflection.getField(explosionPacket, float.class, 0);
private final Reflection.Field<List> e = Reflection.getField(explosionPacket, List.class, 0);
@Override
public Object resetExplosionKnockback(Object packet) {
PacketPlayOutExplosion packetPlayOutExplosion = (PacketPlayOutExplosion) packet;
return new PacketPlayOutExplosion(a.get(packetPlayOutExplosion), b.get(packetPlayOutExplosion), c.get(packetPlayOutExplosion), d.get(packetPlayOutExplosion), e.get(packetPlayOutExplosion), null);
}
}
@@ -1,51 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import de.steamwar.Reflection;
import net.minecraft.network.protocol.game.PacketPlayInFlying;
import net.minecraft.server.level.EntityPlayer;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftPlayer;
import org.bukkit.entity.Player;
public class PlayerMovementWrapper18 implements PlayerMovementWrapper {
@Override
public void setPosition(Player player, Object object) {
PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle();
if (packetPlayInFlying.h) {
entityPlayer.b(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c, packetPlayInFlying.d, packetPlayInFlying.e);
} else {
entityPlayer.e(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c);
}
}
@Override
public Object convertToOut(Player player, Object object) {
PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
Object packet = Reflection.newInstance(teleportPacket);
teleportEntity.set(packet, player.getEntityId());
teleportPosition.set(packet, packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c,
packetPlayInFlying.h ? player.getLocation().getYaw() : packetPlayInFlying.d,
packetPlayInFlying.h ? player.getLocation().getPitch() : packetPlayInFlying.e);
return packet;
}
}
-37
View File
@@ -1,37 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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/>.
*/
plugins {
steamwar.java
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
compileOnly(project(":BauSystem:BauSystem_Main", "default"))
compileOnly(project(":SpigotCore", "default"))
compileOnly(libs.spigotapi)
compileOnly(libs.paperapi)
compileOnly(libs.nms19)
}
@@ -1,134 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import de.steamwar.Reflection;
import de.steamwar.bausystem.features.util.NoClipCommand;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.protocol.game.*;
import net.minecraft.server.level.PlayerInteractManager;
import net.minecraft.world.level.EnumGamemode;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_19_R2.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.List;
public class NMSWrapper19 implements NMSWrapper {
private static final Reflection.Field<EnumGamemode> playerGameMode = Reflection.getField(PlayerInteractManager.class, EnumGamemode.class, 0);
@Override
@SuppressWarnings("deprecation")
public void setInternalGameMode(Player player, GameMode gameMode) {
playerGameMode.set(((CraftPlayer) player).getHandle().d, EnumGamemode.a(gameMode.getValue()));
}
@Override
public void setSlotToItemStack(Player player, Object o) {
PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) o;
int index = packetPlayInSetCreativeSlot.b();
if (index >= 36 && index <= 44) {
index -= 36;
} else if (index > 44) {
index -= 5;
} else if (index <= 8) {
index = index - 8 + 36;
}
player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.c()));
if (index < 9) player.getInventory().setHeldItemSlot(index);
player.updateInventory();
}
private static final Reflection.Field<PacketPlayOutGameStateChange.a> gameStateChangeReason = Reflection.getField(NoClipCommand.gameStateChange, PacketPlayOutGameStateChange.a.class, 12);
@Override
public void setGameStateChangeReason(Object packet) {
gameStateChangeReason.set(packet, PacketPlayOutGameStateChange.d);
}
@Override
public void setPlayerBuildAbilities(Player player) {
((CraftPlayer) player).getHandle().fF().d = true;
((CraftPlayer) player).getHandle().fF().e = true;
}
@Override
public Material pathMaterial() {
return Material.DIRT_PATH;
}
private static final int threshold = 2048;
@Override
public boolean checkItemStack(ItemStack item) {
net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
NBTTagCompound tag = nmsItem.v();
if (tag != null && tag.e("BlockEntityTag")) {
NBTTagCompound blockTag = tag.p("BlockEntityTag");
if (blockTag.e("Items")) {
return drillDown(blockTag.c("Items", 10), 0, 0) > threshold;
}
}
return false;
}
private int drillDown(NBTTagList items, int layer, int start) {
if (layer > 2) return start + threshold;
int invalid = start;
for (NBTBase nbtBase : items) {
if (!(nbtBase instanceof NBTTagCompound))
continue;
NBTTagCompound slot = (NBTTagCompound) nbtBase;
if (slot.e("tag")) {
invalid += slot.f("Count");
NBTTagCompound iTag = slot.p("tag");
if (iTag.e("BlockEntityTag")) {
NBTTagCompound blockTag = iTag.p("BlockEntityTag");
if (blockTag.e("Items")) {
invalid = drillDown(blockTag.c("Items", 10), layer + 1, invalid);
}
}
}
if (invalid > threshold)
break;
}
return invalid;
}
private final Class<?> explosionPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundExplodePacket");
private final Reflection.Field<Double> a = Reflection.getField(explosionPacket, double.class, 0);
private final Reflection.Field<Double> b = Reflection.getField(explosionPacket, double.class, 1);
private final Reflection.Field<Double> c = Reflection.getField(explosionPacket, double.class, 2);
private final Reflection.Field<Float> d = Reflection.getField(explosionPacket, float.class, 0);
private final Reflection.Field<List> e = Reflection.getField(explosionPacket, List.class, 0);
@Override
public Object resetExplosionKnockback(Object packet) {
PacketPlayOutExplosion packetPlayOutExplosion = (PacketPlayOutExplosion) packet;
return new PacketPlayOutExplosion(a.get(packetPlayOutExplosion), b.get(packetPlayOutExplosion), c.get(packetPlayOutExplosion), d.get(packetPlayOutExplosion), e.get(packetPlayOutExplosion), null);
}
}
@@ -1,51 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import de.steamwar.Reflection;
import net.minecraft.network.protocol.game.PacketPlayInFlying;
import net.minecraft.server.level.EntityPlayer;
import org.bukkit.craftbukkit.v1_19_R2.entity.CraftPlayer;
import org.bukkit.entity.Player;
public class PlayerMovementWrapper19 implements PlayerMovementWrapper {
@Override
public void setPosition(Player player, Object object) {
PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle();
if (packetPlayInFlying.h) {
entityPlayer.b(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c, packetPlayInFlying.d, packetPlayInFlying.e);
} else {
entityPlayer.e(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c);
}
}
@Override
public Object convertToOut(Player player, Object object) {
PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
Object packet = Reflection.newInstance(teleportPacket);
teleportEntity.set(packet, player.getEntityId());
teleportPosition.set(packet, packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c,
packetPlayInFlying.h ? player.getLocation().getYaw() : packetPlayInFlying.d,
packetPlayInFlying.h ? player.getLocation().getPitch() : packetPlayInFlying.e);
return packet;
}
}
@@ -1,50 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import com.destroystokyo.paper.event.server.ServerTickEndEvent;
import com.destroystokyo.paper.event.server.ServerTickStartEvent;
import de.steamwar.bausystem.BauSystem;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class TickListener19 implements TickListener, Listener {
private boolean tickStartRan = false;
public TickListener19() {
Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance());
}
@EventHandler
public void onServerTickStart(ServerTickStartEvent event) {
if (TickManager.impl.isFrozen()) return;
Bukkit.getPluginManager().callEvent(new TickStartEvent());
tickStartRan = true;
}
@EventHandler
public void onServerTickEnd(ServerTickEndEvent event) {
if (!tickStartRan) return;
Bukkit.getPluginManager().callEvent(new TickEndEvent());
tickStartRan = false;
}
}
-36
View File
@@ -1,36 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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/>.
*/
plugins {
steamwar.java
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
compileOnly(project(":BauSystem:BauSystem_Main", "default"))
compileOnly(project(":SpigotCore", "default"))
compileOnly(libs.spigotapi)
compileOnly(libs.nms20)
}
@@ -1,136 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import de.steamwar.Reflection;
import de.steamwar.bausystem.features.util.NoClipCommand;
import net.minecraft.nbt.NBTBase;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.network.protocol.game.PacketPlayInSetCreativeSlot;
import net.minecraft.network.protocol.game.PacketPlayOutExplosion;
import net.minecraft.network.protocol.game.PacketPlayOutGameStateChange;
import net.minecraft.server.level.PlayerInteractManager;
import net.minecraft.world.level.EnumGamemode;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.List;
public class NMSWrapper20 implements NMSWrapper {
private static final Reflection.Field<EnumGamemode> playerGameMode = Reflection.getField(PlayerInteractManager.class, EnumGamemode.class, 0);
@Override
@SuppressWarnings("deprecation")
public void setInternalGameMode(Player player, GameMode gameMode) {
playerGameMode.set(((CraftPlayer) player).getHandle().e, EnumGamemode.a(gameMode.getValue()));
}
@Override
public void setSlotToItemStack(Player player, Object o) {
PacketPlayInSetCreativeSlot packetPlayInSetCreativeSlot = (PacketPlayInSetCreativeSlot) o;
int index = packetPlayInSetCreativeSlot.a();
if (index >= 36 && index <= 44) {
index -= 36;
} else if (index > 44) {
index -= 5;
} else if (index <= 8) {
index = index - 8 + 36;
}
player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.c()));
if (index < 9) player.getInventory().setHeldItemSlot(index);
player.updateInventory();
}
private static final Reflection.Field<PacketPlayOutGameStateChange.a> gameStateChangeReason = Reflection.getField(NoClipCommand.gameStateChange, PacketPlayOutGameStateChange.a.class, 12);
@Override
public void setGameStateChangeReason(Object packet) {
gameStateChangeReason.set(packet, PacketPlayOutGameStateChange.d);
}
@Override
public void setPlayerBuildAbilities(Player player) {
((CraftPlayer) player).getHandle().fO().d = true;
((CraftPlayer) player).getHandle().fO().e = true;
}
@Override
public Material pathMaterial() {
return Material.DIRT_PATH;
}
private static final int threshold = 2048;
@Override
public boolean checkItemStack(ItemStack item) {
net.minecraft.world.item.ItemStack nmsItem = CraftItemStack.asNMSCopy(item);
NBTTagCompound tag = nmsItem.v();
if (tag != null && tag.e("BlockEntityTag")) {
NBTTagCompound blockTag = tag.p("BlockEntityTag");
if (blockTag.e("Items")) {
return drillDown(blockTag.c("Items", 10), 0, 0) > threshold;
}
}
return false;
}
private int drillDown(NBTTagList items, int layer, int start) {
if (layer > 2) return start + threshold;
int invalid = start;
for (NBTBase nbtBase : items) {
if (!(nbtBase instanceof NBTTagCompound))
continue;
NBTTagCompound slot = (NBTTagCompound) nbtBase;
if (slot.e("tag")) {
invalid += slot.f("Count");
NBTTagCompound iTag = slot.p("tag");
if (iTag.e("BlockEntityTag")) {
NBTTagCompound blockTag = iTag.p("BlockEntityTag");
if (blockTag.e("Items")) {
invalid = drillDown(blockTag.c("Items", 10), layer + 1, invalid);
}
}
}
if (invalid > threshold)
break;
}
return invalid;
}
private final Class<?> explosionPacket = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundExplodePacket");
private final Reflection.Field<Double> a = Reflection.getField(explosionPacket, double.class, 0);
private final Reflection.Field<Double> b = Reflection.getField(explosionPacket, double.class, 1);
private final Reflection.Field<Double> c = Reflection.getField(explosionPacket, double.class, 2);
private final Reflection.Field<Float> d = Reflection.getField(explosionPacket, float.class, 0);
private final Reflection.Field<List> e = Reflection.getField(explosionPacket, List.class, 0);
@Override
public Object resetExplosionKnockback(Object packet) {
PacketPlayOutExplosion packetPlayOutExplosion = (PacketPlayOutExplosion) packet;
return new PacketPlayOutExplosion(a.get(packetPlayOutExplosion), b.get(packetPlayOutExplosion), c.get(packetPlayOutExplosion), d.get(packetPlayOutExplosion), e.get(packetPlayOutExplosion), null);
}
}
@@ -1,43 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
public class PlaceItemWrapper20 implements PlaceItemWrapper {
public PlaceItemWrapper20() {
for (Material material : Material.values()) {
if (!material.isBlock()) continue;
if (material.isLegacy()) continue;
BlockData blockData = material.createBlockData();
Material placementMaterial = blockData.getPlacementMaterial();
if (material == placementMaterial) continue;
if (placementMaterial == Material.AIR) continue;
if (placementMaterial.isItem() && !placementMaterial.isBlock()) {
ITEM_MATERIAL_TO_BLOCK_MATERIAL.put(placementMaterial, material);
}
if (material.name().contains("WALL")) {
BLOCK_MATERIAL_TO_WALL_BLOCK_MATERIAL.put(placementMaterial, material);
}
}
}
}
@@ -1,51 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import de.steamwar.Reflection;
import net.minecraft.network.protocol.game.PacketPlayInFlying;
import net.minecraft.server.level.EntityPlayer;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
public class PlayerMovementWrapper20 implements PlayerMovementWrapper {
@Override
public void setPosition(Player player, Object object) {
PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
EntityPlayer entityPlayer = ((CraftPlayer) player).getHandle();
if (packetPlayInFlying.h) {
entityPlayer.b(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c, packetPlayInFlying.d, packetPlayInFlying.e);
} else {
entityPlayer.e(packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c);
}
}
@Override
public Object convertToOut(Player player, Object object) {
PacketPlayInFlying packetPlayInFlying = ((PacketPlayInFlying) object);
Object packet = Reflection.newInstance(teleportPacket);
teleportEntity.set(packet, player.getEntityId());
teleportPosition.set(packet, packetPlayInFlying.a, packetPlayInFlying.b, packetPlayInFlying.c,
packetPlayInFlying.h ? player.getLocation().getYaw() : packetPlayInFlying.d,
packetPlayInFlying.h ? player.getLocation().getPitch() : packetPlayInFlying.e);
return packet;
}
}
-36
View File
@@ -1,36 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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/>.
*/
plugins {
steamwar.java
}
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
dependencies {
compileOnly(project(":BauSystem:BauSystem_Main", "default"))
compileOnly(project(":SpigotCore", "default"))
compileOnly(libs.paperapi21)
compileOnly(libs.nms21)
}
@@ -1,133 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import de.steamwar.Reflection;
import de.steamwar.bausystem.features.util.NoClipCommand;
import io.papermc.paper.datacomponent.DataComponentTypes;
import io.papermc.paper.datacomponent.item.ItemContainerContents;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.world.entity.player.Abilities;
import net.minecraft.world.level.GameType;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.List;
import java.util.Optional;
public class NMSWrapper21 implements NMSWrapper {
private static final Reflection.Field<ServerPlayerGameMode> playerInteractManager = Reflection.getField(ServerPlayer.class, null, ServerPlayerGameMode.class);
private static final Reflection.Field<GameType> playerGameMode = Reflection.getField(ServerPlayerGameMode.class, GameType.class, 0);
@Override
public void setInternalGameMode(Player player, GameMode gameMode) {
playerGameMode.set(playerInteractManager.get(((CraftPlayer) player).getHandle()), GameType.byId(gameMode.getValue()));
}
@Override
public void setSlotToItemStack(Player player, Object o) {
ClientboundContainerSetSlotPacket packetPlayInSetCreativeSlot = (ClientboundContainerSetSlotPacket) o;
int index = packetPlayInSetCreativeSlot.getSlot();
if (index >= 36 && index <= 44) {
index -= 36;
} else if (index > 44) {
index -= 5;
} else if (index <= 8) {
index = index - 8 + 36;
}
player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.getItem()));
if (index < 9) player.getInventory().setHeldItemSlot(index);
player.updateInventory();
}
private static final Reflection.Field<ClientboundGameEventPacket.Type> gameStateChangeReason = Reflection.getField(NoClipCommand.gameStateChange, ClientboundGameEventPacket.Type.class, 14);
@Override
public void setGameStateChangeReason(Object packet) {
gameStateChangeReason.set(packet, ClientboundGameEventPacket.CHANGE_GAME_MODE);
}
@Override
public void setPlayerBuildAbilities(Player player) {
Abilities abilities = (((CraftPlayer) player).getHandle()).getAbilities();
abilities.mayBuild = true;
abilities.mayfly = true;
}
@Override
public Material pathMaterial() {
return Material.DIRT_PATH;
}
private static final int threshold = 2048;
@Override
public boolean checkItemStack(ItemStack item) {
ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER);
if (data == null) {
return false;
}
return drillDown(data.contents(), 0, 0) > threshold;
}
private int drillDown(List<ItemStack> items, int layer, int start) {
if (layer > 2) return start + threshold;
int invalid = start;
for (int i = start; i < items.size(); i++) {
ItemStack item = items.get(i);
if (item.isEmpty()) continue;
invalid += item.getAmount();
ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER);
if (data == null) {
continue;
}
List<ItemStack> subItems = data.contents();
if (subItems.size() > 1) {
invalid = drillDown(subItems, layer + 1, invalid);
}
}
return invalid;
}
@Override
public Object resetExplosionKnockback(Object packet) {
ClientboundExplodePacket explosion = (ClientboundExplodePacket) packet;
return new ClientboundExplodePacket(
explosion.center(),
Optional.empty(),
explosion.explosionParticle(),
explosion.explosionSound()
);
}
}
@@ -1,46 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.server.level.ServerPlayer;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
public class PlayerMovementWrapper21 implements PlayerMovementWrapper {
@Override
public void setPosition(Player player, Object object) {
ServerboundMovePlayerPacket packetPlayInFlying = ((ServerboundMovePlayerPacket) object);
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
if (packetPlayInFlying.hasPos) {
serverPlayer.setPosRaw(packetPlayInFlying.x, packetPlayInFlying.y, packetPlayInFlying.z);
}
if (packetPlayInFlying.hasRot) {
serverPlayer.setXRot(packetPlayInFlying.xRot);
serverPlayer.setYRot(packetPlayInFlying.yRot);
}
}
@Override
public Object convertToOut(Player player, Object object) {
return object;
}
}
@@ -1,150 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.bausystem.BauSystem;
import net.minecraft.network.protocol.game.ClientboundTickingStatePacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerTickRateManager;
import net.minecraft.world.TickRateManager;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
public class TickManager21 implements TickManager {
private static final ServerTickRateManager manager = MinecraftServer.getServer().tickRateManager();
private static final Reflection.Field<Integer> frozenTicksToRun = Reflection.getField(TickRateManager.class, int.class, 0);
private static final Reflection.Field<Long> remainingSprintTicks = Reflection.getField(ServerTickRateManager.class, long.class, 0);
private boolean blockTpsPacket = true;
private int totalSteps;
public TickManager21() {
TinyProtocol.instance.addFilter(ClientboundTickingStatePacket.class, this::blockPacket);
}
private Object blockPacket(Player player, Object packet) {
if (blockTpsPacket) {
return new ClientboundTickingStatePacket(20, manager.isFrozen());
} else {
return packet;
}
}
@Override
public boolean canFreeze() {
return true;
}
@Override
public void setBlockTpsPacket(boolean block) {
blockTpsPacket = block;
if (blockTpsPacket) {
ClientboundTickingStatePacket packet = new ClientboundTickingStatePacket(20, manager.isFrozen());
Bukkit.getOnlinePlayers().forEach(player -> TinyProtocol.instance.sendPacket(player, packet));
} else {
ClientboundTickingStatePacket packet = new ClientboundTickingStatePacket(manager.tickrate(), manager.isFrozen());
Bukkit.getOnlinePlayers().forEach(player -> TinyProtocol.instance.sendPacket(player, packet));
}
}
@Override
public void setTickRate(float tickRate) {
if (isFrozen()) {
setFreeze(false);
}
manager.setTickRate(tickRate);
}
@Override
public boolean isFrozen() {
return manager.isFrozen();
}
@Override
public void setFreeze(boolean freeze) {
manager.setFrozen(freeze);
}
@Override
public void stepTicks(int ticks) {
if (manager.isSprinting()) {
manager.stopSprinting();
} else if (manager.isSteppingForward()) {
manager.stopStepping();
}
this.totalSteps = ticks;
manager.setFrozen(true);
manager.stepGameIfPaused(ticks);
manager.setFrozen(false);
Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), (bukkitTask) -> {
if (manager.isSteppingForward()) return;
manager.setFrozen(true);
bukkitTask.cancel();
}, 1, 1);
manager.tick();
}
@Override
public void sprintTicks(int ticks) {
if (manager.isSteppingForward()) {
manager.stopStepping();
} else if (manager.isSprinting()) {
manager.stopSprinting();
}
this.totalSteps = ticks;
manager.requestGameToSprint(ticks, true);
}
@Override
public boolean isSprinting() {
return manager.isSprinting();
}
@Override
public boolean isStepping() {
return manager.isSteppingForward();
}
@Override
public float getTickRate() {
return manager.tickrate();
}
@Override
public long getRemainingTicks() {
if (isSprinting()) {
return remainingSprintTicks.get(manager);
} else {
return frozenTicksToRun.get(manager);
}
}
@Override
public long getDoneTicks() {
return totalSteps - getRemainingTicks();
}
@Override
public long getTotalTicks() {
return totalSteps;
}
}
+5 -3
View File
@@ -26,8 +26,8 @@ tasks.compileJava {
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
dependencies {
@@ -35,10 +35,12 @@ dependencies {
annotationProcessor(libs.classindex)
compileOnly(project(":SpigotCore", "default"))
compileOnly(libs.spigotapi)
compileOnly(libs.axiom)
compileOnly(libs.authlib)
compileOnly(libs.paperapi21)
compileOnly(libs.nms21)
compileOnly(libs.fawe18)
implementation(libs.luaj)
@@ -34,7 +34,6 @@ import de.steamwar.bausystem.features.world.BauScoreboard;
import de.steamwar.bausystem.linkage.BauGuiItem;
import de.steamwar.bausystem.region.RegionSystem;
import de.steamwar.bausystem.utils.ScoreboardElement;
import de.steamwar.bausystem.utils.TickListener;
import de.steamwar.bausystem.utils.TickManager;
import de.steamwar.bausystem.worlddata.WorldData;
import de.steamwar.command.AbstractValidator;
@@ -127,8 +126,6 @@ public class BauSystem extends JavaPlugin implements Listener {
return;
}
TickListener.impl.init();
TraceManager.instance.init();
TraceRecorder.instance.init();
@@ -31,10 +31,7 @@ import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.stream.Collectors;
public class DesignEndStone {
@@ -56,11 +53,10 @@ public class DesignEndStone {
this.maxY = region.getBuildArea().getMaxPoint(false).getY();
this.maxZ = region.getBuildArea().getMaxPoint(false).getZ();
limited = region.getGameModeConfig().Schematic.Limited
.entrySet()
.stream()
.filter(entry -> entry.getValue() == 0)
.flatMap(entry -> entry.getKey().stream())
limited = Arrays.stream(Material.values())
.filter(Material::isBlock)
.filter(material -> !material.isLegacy())
.filter(material -> material.getBlastResistance() > region.getGameModeConfig().Schematic.MaxDesignBlastResistance)
.collect(Collectors.toSet());
calculateFromBottom = region.getGameModeConfig().Arena.NoFloor;
@@ -0,0 +1,63 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 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.bausystem.features.dev;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.IOException;
@Linked
public class CreateKitCommand extends SWCommand {
public CreateKitCommand() {
super("createkit");
if (!BauSystem.DEV_SERVER) unregister();
}
@Register
public void onCommand(Player player, String name) {
YamlConfiguration yaml = new YamlConfiguration();
yaml.set("Items", player.getInventory().getContents());
yaml.set("Armor", player.getInventory().getArmorContents());
yaml.set("Effects", player.getActivePotionEffects());
yaml.set("LeaderAllowed", true);
yaml.set("MemberAllowed", true);
yaml.set("EnterStage", 0);
yaml.set("TNT", true);
YamlConfiguration kits = new YamlConfiguration();
kits.set("Kits." + name, yaml);
try {
kits.save(new File("new.kits.yaml"));
player.sendMessage("Kit created!");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
@@ -19,7 +19,6 @@
package de.steamwar.bausystem.features.observer;
import de.steamwar.Reflection;
import de.steamwar.bausystem.region.Point;
import de.steamwar.core.SWPlayer;
import org.bukkit.Location;
@@ -29,8 +28,9 @@ import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.Bisected;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Observer;
import org.bukkit.block.data.type.*;
import org.bukkit.block.data.type.Observer;
import org.bukkit.craftbukkit.block.impl.CraftPoweredRail;
import org.bukkit.entity.Player;
import java.util.*;
@@ -171,7 +171,6 @@ public class ObserverTracer implements SWPlayer.Component {
}
}
private static final Class<?> craftPoweredRail = Reflection.getClass("org.bukkit.craftbukkit.block.impl.CraftPoweredRail");
private boolean checkAllowed(Block block, BlockData blockData) {
if (checkMaterial(block)) return true;
if (block.getType() == Material.BELL) {
@@ -180,7 +179,7 @@ public class ObserverTracer implements SWPlayer.Component {
return blockData instanceof Door
|| blockData instanceof Gate
|| craftPoweredRail.isInstance(blockData)
|| blockData instanceof CraftPoweredRail
|| blockData instanceof TrapDoor
|| blockData instanceof GlassPane;
}
@@ -46,6 +46,16 @@ import org.bukkit.event.player.PlayerInteractEvent;
@Linked
public class FreezeListener implements Listener, ScoreboardElement {
@EventHandler
public void onBlockExplode(BlockExplodeEvent e) {
if (Region.getRegion(e.getBlock().getLocation()).getRegionData().get(Flag.FREEZE).isWithDefault(FreezeMode.INACTIVE)) return;
e.setCancelled(true);
BlockState state = e.getBlock().getState();
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
state.update(true, false);
}, 1L);
}
@EventHandler
public void onEntitySpawn(EntitySpawnEvent e) {
if (Region.getRegion(e.getLocation()).getRegionData().get(Flag.FREEZE).isWithDefault(FreezeMode.INACTIVE)) return;
@@ -67,11 +67,7 @@ public class TNTListener implements Listener, ScoreboardElement {
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onExplode(EntityExplodeEvent event) {
if (!(event.getEntity() instanceof TNTPrimed)) {
event.blockList().clear();
return;
}
explode(event.blockList(), true);
explode(event.blockList(), event.getEntity() instanceof TNTPrimed);
}
@Override
@@ -20,7 +20,6 @@
package de.steamwar.bausystem.features.simulator;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.SWUtils;
@@ -48,6 +47,7 @@ import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MinVersion;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -72,9 +72,6 @@ import java.util.stream.Collectors;
public class SimulatorCursor implements Listener {
private static final World WORLD = Bukkit.getWorlds().get(0);
private Class<?> position = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos");
private Class<?> look = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Rot");
private Class<?> positionLook = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot");
private static Map<Player, CursorType> cursorType = Collections.synchronizedMap(new HashMap<>());
private static Map<Player, REntityServer> cursors = Collections.synchronizedMap(new HashMap<>());
@@ -89,9 +86,9 @@ public class SimulatorCursor implements Listener {
calcCursor(player);
return object;
};
TinyProtocol.instance.addFilter(position, function);
TinyProtocol.instance.addFilter(look, function);
TinyProtocol.instance.addFilter(positionLook, function);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, function);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Rot.class, function);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, function);
}
@EventHandler
@@ -19,14 +19,15 @@
package de.steamwar.bausystem.features.simulator.execute;
import com.destroystokyo.paper.event.server.ServerTickEndEvent;
import com.destroystokyo.paper.event.server.ServerTickStartEvent;
import de.steamwar.bausystem.features.simulator.data.Simulator;
import de.steamwar.bausystem.features.simulator.data.SimulatorElement;
import de.steamwar.bausystem.features.simulator.data.SimulatorGroup;
import de.steamwar.bausystem.features.tpslimit.TPSUtils;
import de.steamwar.bausystem.features.tracer.TraceRecorder;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.TickEndEvent;
import de.steamwar.bausystem.utils.TickStartEvent;
import de.steamwar.bausystem.utils.TickManager;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MinVersion;
import org.bukkit.Bukkit;
@@ -109,8 +110,13 @@ public class SimulatorExecutor implements Listener {
return true;
}
private boolean tickStartRan = false;
@EventHandler
public void onTickStart(TickStartEvent event) {
public void onServerTickStart(ServerTickStartEvent event) {
if (TickManager.impl.isFrozen()) return;
tickStartRan = true;
long currentTick = TPSUtils.currentRealTick.get();
Map<Integer, List<SimulatorAction>> actionsToRun = tickStartActions.remove(currentTick);
if (actionsToRun == null) return;
@@ -123,7 +129,10 @@ public class SimulatorExecutor implements Listener {
}
@EventHandler
public void onTickEnd(TickEndEvent event) {
public void onServerTickEnd(ServerTickEndEvent event) {
if (!tickStartRan) return;
tickStartRan = false;
long currentTick = TPSUtils.currentRealTick.get() - 1;
List<SimulatorAction> actionsToRun = tickEndActions.remove(currentTick);
if (actionsToRun == null) return;
@@ -150,11 +150,13 @@ public class SimulatorObserverGui extends SimulatorScrollGui<ObserverPhase> {
Consumer<Integer> setter = observerPhase::setTickOffset;
return new SWItem[] {
new SWItem(SWItem.getDye(getter.get() < max ? 10 : 8), "§e+1", Arrays.asList("§7Shift§8:§e +5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
setter.accept(Math.min(max, getter.get() + (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED),
observer,
new SWItem(SWItem.getDye(getter.get() > min ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8:§e -5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
setter.accept(Math.max(min, getter.get() - (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED),
@@ -32,6 +32,7 @@ import de.steamwar.inventory.SWItem;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import java.util.Arrays;
@@ -97,6 +98,7 @@ public class SimulatorObserverPhaseSettingsGui extends SimulatorBaseGui {
//Tick Offset
int offset = observer.getTickOffset();
inventory.setItem(10, new SWItem(SWItem.getDye(offset < max ? 10 : 8), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
observer.setTickOffset(Math.min(max, offset + (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -113,6 +115,7 @@ public class SimulatorObserverPhaseSettingsGui extends SimulatorBaseGui {
inventory.setItem(19, offsetItem);
inventory.setItem(28, new SWItem(SWItem.getDye(offset > min ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
observer.setTickOffset(Math.max(min, offset - (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
@@ -120,6 +123,7 @@ public class SimulatorObserverPhaseSettingsGui extends SimulatorBaseGui {
//Order
int order = observer.getOrder();
inventory.setItem(13, new SWItem(SWItem.getDye(order < SimulatorPhase.ORDER_LIMIT ? 10 : 8), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
observer.setOrder(Math.min(SimulatorPhase.ORDER_LIMIT, order + (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -138,6 +142,7 @@ public class SimulatorObserverPhaseSettingsGui extends SimulatorBaseGui {
inventory.setItem(22, orderItem);
inventory.setItem(31, new SWItem(SWItem.getDye(order > -SimulatorPhase.ORDER_LIMIT ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
observer.setOrder(Math.max(-SimulatorPhase.ORDER_LIMIT, order - (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
@@ -28,6 +28,7 @@ import de.steamwar.data.CMDs;
import de.steamwar.inventory.SWItem;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import java.util.Arrays;
@@ -67,6 +68,7 @@ public class SimulatorObserverSettingsGui extends SimulatorBaseGui {
// Base Tick
int baseTicks = observer.getBaseTick();
inventory.setItem(9, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
observer.changeBaseTicks(clickType.isShiftClick() ? 5 : 1);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -81,6 +83,7 @@ public class SimulatorObserverSettingsGui extends SimulatorBaseGui {
baseTick.getItemStack().setAmount(Math.max(1, Math.min(baseTicks, 64)));
inventory.setItem(18, baseTick);
inventory.setItem(27, new SWItem(SWItem.getDye(baseTicks > 0 ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
if (baseTicks - (clickType.isShiftClick() ? 5 : 1) < 0) {
observer.changeBaseTicks(-baseTicks);
} else {
@@ -91,6 +94,7 @@ public class SimulatorObserverSettingsGui extends SimulatorBaseGui {
//Pos X
inventory.setItem(15, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
observer.move(clickType.isShiftClick() ? 5 : 1, 0, 0);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -102,12 +106,14 @@ public class SimulatorObserverSettingsGui extends SimulatorBaseGui {
}, this).open();
}));
inventory.setItem(33, new SWItem(SWItem.getDye(1), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
observer.move(clickType.isShiftClick() ? -5 : -1, 0, 0);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
//Pos Y
inventory.setItem(16, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
observer.move(0, clickType.isShiftClick() ? 5 : 1, 0);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.ENABLED_OR_DISABLED));
@@ -119,12 +125,14 @@ public class SimulatorObserverSettingsGui extends SimulatorBaseGui {
}, this).open();
}));
inventory.setItem(34, new SWItem(SWItem.getDye(1), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
observer.move(0, clickType.isShiftClick() ? -5 : -1, 0);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
//Pos Z
inventory.setItem(17, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
observer.move(0, 0, clickType.isShiftClick() ? 5 : 1);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.ENABLED_OR_DISABLED));
@@ -136,6 +144,7 @@ public class SimulatorObserverSettingsGui extends SimulatorBaseGui {
}, this).open();
}));
inventory.setItem(35, new SWItem(SWItem.getDye(1), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
observer.move(0, 0, clickType.isShiftClick() ? -5 : -1);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
@@ -165,11 +165,13 @@ public class SimulatorRedstoneGui extends SimulatorScrollGui<SimulatorRedstoneGu
Consumer<Integer> setter = redstoneSubPhase.place ? redstoneSubPhase.phase::setTickOffset : redstoneSubPhase.phase::setLifetime;
return new SWItem[] {
new SWItem(SWItem.getDye(getter.get() < max ? 10 : 8), "§e+1", Arrays.asList("§7Shift§8:§e +5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
setter.accept(Math.min(max, getter.get() + (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED),
redstone,
new SWItem(SWItem.getDye(getter.get() > min ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8:§e -5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
setter.accept(Math.max(min, getter.get() - (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED),
@@ -31,6 +31,7 @@ import de.steamwar.data.CMDs;
import de.steamwar.inventory.SWItem;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import java.util.Arrays;
@@ -98,6 +99,7 @@ public class SimulatorRedstonePhaseSettingsGui extends SimulatorBaseGui {
//Tick Offset
int offset = redstone.getTickOffset();
inventory.setItem(10, new SWItem(SWItem.getDye(offset < maxOffset ? 10 : 8), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.setTickOffset(Math.min(maxOffset, offset + (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -114,6 +116,7 @@ public class SimulatorRedstonePhaseSettingsGui extends SimulatorBaseGui {
inventory.setItem(19, offsetItem);
inventory.setItem(28, new SWItem(SWItem.getDye(offset > min ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.setTickOffset(Math.max(min, offset - (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
@@ -121,6 +124,7 @@ public class SimulatorRedstonePhaseSettingsGui extends SimulatorBaseGui {
//Lifetime
int lifetime = redstone.getLifetime();
inventory.setItem(11, new SWItem(SWItem.getDye(lifetime < maxLifetime ? 10 : 8), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.setLifetime(Math.min(maxLifetime, lifetime + (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -137,6 +141,7 @@ public class SimulatorRedstonePhaseSettingsGui extends SimulatorBaseGui {
inventory.setItem(20, lifetimeItem);
inventory.setItem(29, new SWItem(SWItem.getDye(lifetime > 0 ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.setLifetime(Math.max(0, lifetime - (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
@@ -144,6 +149,7 @@ public class SimulatorRedstonePhaseSettingsGui extends SimulatorBaseGui {
//Order
int order = redstone.getOrder();
inventory.setItem(13, new SWItem(SWItem.getDye(order < SimulatorPhase.ORDER_LIMIT ? 10 : 8), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.setOrder(Math.min(SimulatorPhase.ORDER_LIMIT, order + (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -162,6 +168,7 @@ public class SimulatorRedstonePhaseSettingsGui extends SimulatorBaseGui {
inventory.setItem(22, orderItem);
inventory.setItem(31, new SWItem(SWItem.getDye(order > -SimulatorPhase.ORDER_LIMIT ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.setOrder(Math.max(-SimulatorPhase.ORDER_LIMIT, order - (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
@@ -28,6 +28,7 @@ import de.steamwar.data.CMDs;
import de.steamwar.inventory.SWItem;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import java.util.Arrays;
@@ -66,6 +67,7 @@ public class SimulatorRedstoneSettingsGui extends SimulatorBaseGui {
// Base Tick
int baseTicks = redstone.getBaseTick();
inventory.setItem(9, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.changeBaseTicks(clickType.isShiftClick() ? 5 : 1);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -80,6 +82,7 @@ public class SimulatorRedstoneSettingsGui extends SimulatorBaseGui {
baseTick.getItemStack().setAmount(Math.max(1, Math.min(baseTicks, 64)));
inventory.setItem(18, baseTick);
inventory.setItem(27, new SWItem(SWItem.getDye(baseTicks > 0 ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
if (baseTicks - (clickType.isShiftClick() ? 5 : 1) < 0) {
redstone.changeBaseTicks(-baseTicks);
} else {
@@ -90,6 +93,7 @@ public class SimulatorRedstoneSettingsGui extends SimulatorBaseGui {
//Pos X
inventory.setItem(15, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.move(clickType.isShiftClick() ? 5 : 1, 0, 0);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -101,12 +105,14 @@ public class SimulatorRedstoneSettingsGui extends SimulatorBaseGui {
}, this).open();
}));
inventory.setItem(33, new SWItem(SWItem.getDye(1), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.move(clickType.isShiftClick() ? -5 : -1, 0, 0);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
//Pos Y
inventory.setItem(16, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.move(0, clickType.isShiftClick() ? 5 : 1, 0);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -118,12 +124,14 @@ public class SimulatorRedstoneSettingsGui extends SimulatorBaseGui {
}, this).open();
}));
inventory.setItem(34, new SWItem(SWItem.getDye(1), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.move(0, clickType.isShiftClick() ? -5 : -1, 0);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
//Pos Z
inventory.setItem(17, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.move(0, 0, clickType.isShiftClick() ? 5 : 1);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -135,6 +143,7 @@ public class SimulatorRedstoneSettingsGui extends SimulatorBaseGui {
}, this).open();
}));
inventory.setItem(35, new SWItem(SWItem.getDye(1), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
redstone.move(0, 0, clickType.isShiftClick() ? -5 : -1);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
@@ -138,11 +138,13 @@ public class SimulatorTNTGui extends SimulatorScrollGui<TNTPhase> {
return new SWItem[]{
new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8:§e +5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tntSetting.setCount(tntSetting.getCount() + (clickType.isShiftClick() ? 5 : 1));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED),
tnt,
new SWItem(SWItem.getDye(tntSetting.getCount() > 1 ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8:§e -5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tntSetting.setCount(Math.max(1, tntSetting.getCount() - (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED),
@@ -31,6 +31,7 @@ import de.steamwar.data.CMDs;
import de.steamwar.inventory.SWItem;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import java.util.Arrays;
@@ -78,6 +79,7 @@ public class SimulatorTNTPhaseSettingsGui extends SimulatorBaseGui {
//Count
int count = tnt.getCount();
inventory.setItem(9, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.setCount(count + (clickType.isShiftClick() ? 5 : 1));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -94,6 +96,7 @@ public class SimulatorTNTPhaseSettingsGui extends SimulatorBaseGui {
inventory.setItem(18, countItem);
inventory.setItem(27, new SWItem(SWItem.getDye(count > 1 ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.setCount(Math.max(1, count - (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
@@ -101,6 +104,7 @@ public class SimulatorTNTPhaseSettingsGui extends SimulatorBaseGui {
//Tick Offset
int offset = tnt.getTickOffset();
inventory.setItem(10, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.setTickOffset(offset + (clickType.isShiftClick() ? 5 : 1));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -117,6 +121,7 @@ public class SimulatorTNTPhaseSettingsGui extends SimulatorBaseGui {
inventory.setItem(19, offsetItem);
inventory.setItem(28, new SWItem(SWItem.getDye(offset > 0 ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.setTickOffset(Math.max(0, offset - (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
@@ -124,6 +129,7 @@ public class SimulatorTNTPhaseSettingsGui extends SimulatorBaseGui {
//Lifetime
int lifetime = tnt.getLifetime();
inventory.setItem(11, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.setLifetime(lifetime + (clickType.isShiftClick() ? 5 : 1));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -140,6 +146,7 @@ public class SimulatorTNTPhaseSettingsGui extends SimulatorBaseGui {
inventory.setItem(20, lifetimeItem);
inventory.setItem(29, new SWItem(SWItem.getDye(lifetime > 0 ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.setLifetime(Math.max(1, lifetime - (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
@@ -147,6 +154,7 @@ public class SimulatorTNTPhaseSettingsGui extends SimulatorBaseGui {
//Order
int order = tnt.getOrder();
inventory.setItem(13, new SWItem(SWItem.getDye(order < SimulatorPhase.ORDER_LIMIT ? 10 : 8), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.setOrder(Math.min(SimulatorPhase.ORDER_LIMIT, order + (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -165,30 +173,35 @@ public class SimulatorTNTPhaseSettingsGui extends SimulatorBaseGui {
inventory.setItem(22, orderItem);
inventory.setItem(31, new SWItem(SWItem.getDye(order > -SimulatorPhase.ORDER_LIMIT ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.setOrder(Math.max(-SimulatorPhase.ORDER_LIMIT, order - (clickType.isShiftClick() ? 5 : 1)));
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
//Jump
SWItem jumpX = new SWItem(tnt.isXJump() ? Material.LIME_WOOL : Material.RED_WOOL, "§7TNT §eJump X§8: " + (tnt.isZJump() ? "§aon" : "§coff"), clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.setXJump(!tnt.isXJump());
SimulatorWatcher.update(simulator);
});
inventory.setItem(33, jumpX);
SWItem jumpY = new SWItem(tnt.isYJump() ? Material.LIME_WOOL : Material.RED_WOOL, "§7TNT §eJump Y§8: " + (tnt.isYJump() ? "§aon" : "§coff"), clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.setYJump(!tnt.isYJump());
SimulatorWatcher.update(simulator);
});
inventory.setItem(16, jumpY);
SWItem jumpZ = new SWItem(tnt.isZJump() ? Material.LIME_WOOL : Material.RED_WOOL, "§7TNT §eJump Z§8: " + (tnt.isZJump() ? "§aon" : "§coff"), clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.setZJump(!tnt.isZJump());
SimulatorWatcher.update(simulator);
});
inventory.setItem(35, jumpZ);
SWItem jumpAll = new SWItem(Material.TNT, "§7TNT §eJump §8: " + (tnt.hasJump() ? "§aon" : "§coff"), clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.setJump(!tnt.hasJump());
SimulatorWatcher.update(simulator);
});
@@ -28,6 +28,7 @@ import de.steamwar.data.CMDs;
import de.steamwar.inventory.SWItem;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.ClickType;
import java.util.ArrayList;
import java.util.Arrays;
@@ -75,6 +76,7 @@ public class SimulatorTNTSettingsGui extends SimulatorBaseGui {
// Base Tick
int baseTicks = tnt.getBaseTick();
inventory.setItem(9, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.changeBaseTicks(clickType.isShiftClick() ? 5 : 1);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -89,6 +91,7 @@ public class SimulatorTNTSettingsGui extends SimulatorBaseGui {
baseTick.getItemStack().setAmount(Math.max(1, Math.min(baseTicks, 64)));
inventory.setItem(18, baseTick);
inventory.setItem(27, new SWItem(SWItem.getDye(baseTicks > 0 ? 1 : 8), "§e-1", Arrays.asList("§7Shift§8: §e-5"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
if (baseTicks - (clickType.isShiftClick() ? 5 : 1) < 0) {
tnt.changeBaseTicks(-baseTicks);
} else {
@@ -136,6 +139,7 @@ public class SimulatorTNTSettingsGui extends SimulatorBaseGui {
// Pos X
inventory.setItem(15, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+0.0625"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.move(clickType.isShiftClick() ? 0.0625 : 1, 0, 0);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -147,12 +151,14 @@ public class SimulatorTNTSettingsGui extends SimulatorBaseGui {
}, this).open();
}));
inventory.setItem(33, new SWItem(SWItem.getDye(1), "§e-1", Arrays.asList("§7Shift§8: §e-0.0625"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.move(clickType.isShiftClick() ? -0.0625 : -1, 0, 0);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
// Pos Y
inventory.setItem(16, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+0.0625"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.move(0, clickType.isShiftClick() ? 0.0625 : 1, 0);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -164,12 +170,14 @@ public class SimulatorTNTSettingsGui extends SimulatorBaseGui {
}, this).open();
}));
inventory.setItem(34, new SWItem(SWItem.getDye(1), "§e-1", Arrays.asList("§7Shift§8: §e-0.0625"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.move(0, clickType.isShiftClick() ? -0.0625 : -1, 0);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
// Pos Z
inventory.setItem(17, new SWItem(SWItem.getDye(10), "§e+1", Arrays.asList("§7Shift§8: §e+0.0625"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.move(0, 0, clickType.isShiftClick() ? 0.0625 : 1);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.INCREMENT_OR_DISABLED));
@@ -181,6 +189,7 @@ public class SimulatorTNTSettingsGui extends SimulatorBaseGui {
}, this).open();
}));
inventory.setItem(35, new SWItem(SWItem.getDye(1), "§e-1", Arrays.asList("§7Shift§8: §e-0.0625"), false, clickType -> {
if (clickType == ClickType.DOUBLE_CLICK) return;
tnt.move(0, 0, clickType.isShiftClick() ? -0.0625 : -1);
SimulatorWatcher.update(simulator);
}).setCustomModelData(CMDs.Simulator.DECREMENT_OR_DISABLED));
@@ -101,7 +101,7 @@ public class BlockBoundingBox {
addPixel(Material.END_STONE.createBlockData(), 0, 0, 0, 16, 16, 16, null);
addPixel(NMSWrapper.impl.pathMaterial().createBlockData(), 0, 0, 0, 16, 15, 16, createItem("LAUFBAU_BLOCK_GRASS_PATH", NMSWrapper.impl.pathMaterial()));
addPixel(Material.SOUL_SAND.createBlockData(), 0, 0, 0, 16, 14, 16, createItem("LAUFBAU_BLOCK_SOUL_SAND", Material.SOUL_SAND));
addPixel(Material.MUD.createBlockData(), 0, 0, 0, 16, 14, 16, createItem("LAUFBAU_BLOCK_SOUL_SAND", Material.SOUL_SAND));
Cocoa cocoaNorth = (Cocoa) Material.COCOA.createBlockData();
cocoaNorth.setAge(2);
@@ -25,12 +25,14 @@ import de.steamwar.bausystem.shared.Pair;
import de.steamwar.bausystem.utils.WorldEditUtils;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MinVersion;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
@Linked
@MinVersion(19)
public class LaufbauCommand extends SWCommand {
public LaufbauCommand() {
@@ -20,13 +20,13 @@
package de.steamwar.bausystem.features.smartplace;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.configplayer.Config;
import de.steamwar.core.Core;
import de.steamwar.inventory.SWItem;
import de.steamwar.linkage.Linked;
import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
@@ -83,13 +83,11 @@ public class SmartPlaceListener implements Listener {
IGNORED.remove(Material.BARRIER);
}
private static final Class<?> useItem = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundUseItemOnPacket");
private static final Set<Player> SMART_PLACING = new HashSet<>();
private static final Set<Player> WAS_EXECUTED = new HashSet<>();
public SmartPlaceListener() {
TinyProtocol.instance.addFilter(useItem, (player, packet) -> {
TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, (player, packet) -> {
if(!Permission.BUILD.hasPermission(player)) return packet;
if (!Config.getInstance().get(player).getPlainValueOrDefault("smartPlace", false)) return packet;
RayTraceResult rayTraceResult = player.rayTraceBlocks(6);
@@ -135,7 +135,7 @@ public abstract class ViewFlag {
}
Location secoundLocation;
if (previousVelocity.getX() >= previousVelocity.getZ()) {
if (Math.abs(previousVelocity.getX()) >= Math.abs(previousVelocity.getZ())) {
secoundLocation = previous.getLocation().clone().add(delta.getX(), delta.getY(), 0);
} else {
secoundLocation = previous.getLocation().clone().add(0, delta.getY(), delta.getZ());
@@ -198,6 +198,16 @@ public abstract class ViewFlag {
}
};
public static ViewFlag HIGHLIGHT = new ViewFlag(true, false, "highlight", "h") {
@Override
public void modify(REntityServer server, List<TraceEntity> entities) {
for (TraceEntity entity : entities) {
entity.setGlowing(true);
}
}
};
/**
* Name of the flag
*/
@@ -39,10 +39,7 @@ import lombok.Setter;
import org.bukkit.Material;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack;
import java.util.*;
@@ -21,7 +21,6 @@ package de.steamwar.bausystem.features.util;
import com.comphenix.tinyprotocol.TinyProtocol;
import com.mojang.authlib.GameProfile;
import de.steamwar.Reflection;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.tpslimit.TPSUtils;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
@@ -30,6 +29,7 @@ import de.steamwar.command.SWCommand;
import de.steamwar.core.ProtocolWrapper;
import de.steamwar.core.SWPlayer;
import de.steamwar.linkage.Linked;
import net.minecraft.network.protocol.game.*;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.entity.Player;
@@ -45,16 +45,6 @@ import java.util.function.BiFunction;
@Linked
public class NoClipCommand extends SWCommand implements Listener {
public static final Class<?> gameStateChange = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundGameEventPacket");
private static final Reflection.Field<Float> floatFieldAccessor = Reflection.getField(gameStateChange, float.class, 0);
private static final Class<?> position = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos");
private static final Class<?> positionLook = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot");
private static final Class<?> useItem = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundUseItemOnPacket");
private static final Class<?> blockDig = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundPlayerActionPacket");
private static final Class<?> windowClick = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundContainerClickPacket");
private static final Class<?> setSlotStack = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundSetCreativeModeSlotPacket");
public static class NoClipData implements SWPlayer.Component {
private long lastTick = -1;
@@ -75,8 +65,8 @@ public class NoClipCommand extends SWCommand implements Listener {
noClipData.lastTick = TPSUtils.currentTick.get();
return o;
};
TinyProtocol.instance.addFilter(position, first);
TinyProtocol.instance.addFilter(positionLook, first);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, first);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, first);
BiFunction<Player, Object, Object> second = (player, o) -> {
NoClipData noClipData = SWPlayer.of(player).getComponent(NoClipData.class).orElse(null);
@@ -85,9 +75,9 @@ public class NoClipCommand extends SWCommand implements Listener {
noClipData.lastTick = TPSUtils.currentTick.get();
return o;
};
TinyProtocol.instance.addFilter(useItem, second);
TinyProtocol.instance.addFilter(blockDig, second);
TinyProtocol.instance.addFilter(windowClick, second);
TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, second);
TinyProtocol.instance.addFilter(ServerboundPlayerActionPacket.class, second);
TinyProtocol.instance.addFilter(ServerboundContainerClickPacket.class, second);
BiFunction<Player, Object, Object> third = (player, o) -> {
if (SWPlayer.of(player).hasComponent(NoClipData.class)) {
@@ -95,7 +85,7 @@ public class NoClipCommand extends SWCommand implements Listener {
}
return o;
};
TinyProtocol.instance.addFilter(setSlotStack, third);
TinyProtocol.instance.addFilter(ServerboundSetCreativeModeSlotPacket.class, third);
}
@Register(help = true)
@@ -107,13 +97,9 @@ public class NoClipCommand extends SWCommand implements Listener {
player.setGameMode(GameMode.SPECTATOR);
NMSWrapper.impl.setPlayerBuildAbilities(player);
Object gameStateChangeObject = Reflection.newInstance(gameStateChange);
NMSWrapper.impl.setGameStateChangeReason(gameStateChangeObject);
floatFieldAccessor.set(gameStateChangeObject, 1F);
swPlayer.setComponent(new NoClipData());
BauSystem.MESSAGE.send("OTHER_NOCLIP_SLOT_INFO", player);
TinyProtocol.instance.sendPacket(player, gameStateChangeObject);
TinyProtocol.instance.sendPacket(player, new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, 1F));
pseudoGameMode(player, GameMode.SPECTATOR);
}
}
@@ -22,8 +22,8 @@ package de.steamwar.bausystem.features.util.items;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.features.util.KillAllCommand;
import de.steamwar.bausystem.linkage.BauGuiItem;
import de.steamwar.bausystem.features.util.RegionSelectionType;
import de.steamwar.bausystem.linkage.BauGuiItem;
import de.steamwar.inventory.SWItem;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
@@ -19,11 +19,11 @@
package de.steamwar.bausystem.features.world;
import de.steamwar.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.api.Enable;
import net.minecraft.network.protocol.game.ClientboundContainerClosePacket;
import org.bukkit.Bukkit;
import org.bukkit.event.inventory.InventoryType;
@@ -32,8 +32,7 @@ public class AntiCursorReCentering implements Enable {
@Override
public void enable() {
Class<?> closeWindow = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundContainerClosePacket");
TinyProtocol.instance.addFilter(closeWindow, (player, object) -> {
TinyProtocol.instance.addFilter(ClientboundContainerClosePacket.class, (player, object) -> {
if (player.getOpenInventory().getTopInventory().getType() == InventoryType.CRAFTING) {
return object;
}
@@ -19,17 +19,17 @@
package de.steamwar.bausystem.features.world;
import de.steamwar.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.bausystem.utils.NMSWrapper;
import de.steamwar.linkage.Linked;
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
import org.bukkit.GameMode;
@Linked
public class NoCreativeKnockback {
public NoCreativeKnockback() {
TinyProtocol.instance.addFilter(Reflection.getClass("net.minecraft.network.protocol.game.ClientboundExplodePacket"), (player, o) -> {
TinyProtocol.instance.addFilter(ClientboundExplodePacket.class, (player, o) -> {
if (player.getGameMode() != GameMode.CREATIVE) return o;
return NMSWrapper.impl.resetExplosionKnockback(o);
});
@@ -19,13 +19,14 @@
package de.steamwar.bausystem.features.world;
import de.steamwar.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.utils.PlaceItemUtils;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MinVersion;
import net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket;
import net.minecraft.network.protocol.game.ServerboundSignUpdatePacket;
import net.minecraft.server.level.ServerLevel;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
@@ -37,6 +38,8 @@ import org.bukkit.block.data.Directional;
import org.bukkit.block.data.Rotatable;
import org.bukkit.block.sign.Side;
import org.bukkit.block.sign.SignSide;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.CraftBlock;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
@@ -45,24 +48,7 @@ import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.util.Vector;
@Linked
@MinVersion(20)
public class SignEditFrom20 implements Listener {
private static final Class<?> blockPosition = Reflection.getClass("net.minecraft.core.BlockPos");
private static final Class<?> craftBlock = Reflection.getClass("org.bukkit.craftbukkit.block.CraftBlock");
private static final Class<?> craftWorld = Reflection.getClass("org.bukkit.craftbukkit.CraftWorld");
private static final Class<?> generatorAccess = Reflection.getClass("net.minecraft.world.level.LevelAccessor");
private static final Reflection.Method getPosition = Reflection.getTypedMethod(craftBlock, "getPosition", blockPosition);
private static final Reflection.Method getWorldHandle = Reflection.getTypedMethod(craftWorld, "getHandle", null);
private static final Reflection.Method at = Reflection.getTypedMethod(craftBlock, "at", craftBlock, generatorAccess, blockPosition);
private static final Class<?> openSign = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket");
private static final Reflection.Field<?> blockPositionFieldAccessor = Reflection.getField(openSign, blockPosition, 0);
private static final Reflection.Field<?> sideFieldAccessor = Reflection.getField(openSign, boolean.class, 0);
private static final Class<?> updateSign = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundSignUpdatePacket");
private static final Reflection.Field<?> getBlockPositionFieldAccessor = Reflection.getField(updateSign, blockPosition, 0);
private static final Reflection.Field<String[]> stringFieldAccessor = Reflection.getField(updateSign, String[].class, 0);
public class SignEdit implements Listener {
@EventHandler
public void editSign(PlayerInteractEvent event) {
@@ -93,10 +79,8 @@ public class SignEditFrom20 implements Listener {
sign.update(true);
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
Object openSignObject = Reflection.newInstance(openSign);
blockPositionFieldAccessor.set(openSignObject, getPosition.invoke(block));
sideFieldAccessor.set(openSignObject, side == Side.FRONT);
TinyProtocol.instance.sendPacket(player, openSignObject);
ClientboundOpenSignEditorPacket packet = new ClientboundOpenSignEditorPacket(((CraftBlock) block).getPosition(), side == Side.FRONT);
TinyProtocol.instance.sendPacket(player, packet);
}, 1);
}
@@ -117,14 +101,14 @@ public class SignEditFrom20 implements Listener {
}
{
TinyProtocol.instance.addFilter(updateSign, (player, o) -> {
TinyProtocol.instance.addTypedFilter(ServerboundSignUpdatePacket.class, (player, o) -> {
Bukkit.getScheduler().runTask(BauSystem.getInstance(), () -> {
String[] lines = stringFieldAccessor.get(o);
Block signLoc = (Block) at.invoke(null, getWorldHandle.invoke(player.getWorld()), getBlockPositionFieldAccessor.get(o));
ServerLevel serverLevel = ((CraftWorld) player.getWorld()).getHandle();
Block signLoc = CraftBlock.at(serverLevel, o.getPos());
if (!signLoc.getType().name().contains("SIGN"))
return;
String[] lines = o.getLines();
Sign sign = ((Sign) signLoc.getState());
SignSide signSide = sign.getSide(signSide(player, signLoc));
for (int i = 0; i < lines.length; i++) {
@@ -1,105 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.features.world;
import de.steamwar.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MaxVersion;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.Sign;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
@Linked
@MaxVersion(19)
public class SignEditUntil19 implements Listener {
private static final Class<?> blockPosition = Reflection.getClass("net.minecraft.core.BlockPos");
private static final Class<?> craftBlock = Reflection.getClass("org.bukkit.craftbukkit.block.CraftBlock");
private static final Class<?> craftWorld = Reflection.getClass("org.bukkit.craftbukkit.CraftWorld");
private static final Class<?> generatorAccess = Reflection.getClass("net.minecraft.world.level.LevelAccessor");
private static final Reflection.Method getPosition = Reflection.getTypedMethod(craftBlock, "getPosition", blockPosition);
private static final Reflection.Method getWorldHandle = Reflection.getTypedMethod(craftWorld, "getHandle", null);
private static final Reflection.Method at = Reflection.getTypedMethod(craftBlock, "at", craftBlock, generatorAccess, blockPosition);
private static final Class<?> openSign = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket");
private static final Reflection.Field<?> blockPositionFieldAccessor = Reflection.getField(openSign, blockPosition, 0);
private static final Class<?> updateSign = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundSignUpdatePacket");
private static final Reflection.Field<?> getBlockPositionFieldAccessor = Reflection.getField(updateSign, blockPosition, 0);
private static final Reflection.Field<String[]> stringFieldAccessor = Reflection.getField(updateSign, String[].class, 0);
@EventHandler
public void editSign(PlayerInteractEvent event) {
if (!event.getPlayer().isSneaking()) return;
if (event.getClickedBlock() == null || !event.getClickedBlock().getType().name().contains("SIGN")) return;
if (event.getAction() == Action.RIGHT_CLICK_BLOCK && (event.getItem() == null || event.getItem().getType() == Material.AIR) || event.getAction() == Action.LEFT_CLICK_BLOCK) {
event.setCancelled(true);
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
edit(event.getPlayer(), event.getClickedBlock());
}, 1);
}
}
private void edit(Player player, Block block) {
if (!Permission.BUILD.hasPermission(player)) return;
Sign sign = (org.bukkit.block.Sign) block.getState();
String[] lines = sign.getLines();
for (int i = 0; i < lines.length; i++) {
sign.setLine(i, lines[i].replace('§', '&'));
}
sign.update(true);
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
Object openSignObject = Reflection.newInstance(openSign);
blockPositionFieldAccessor.set(openSignObject, getPosition.invoke(block));
TinyProtocol.instance.sendPacket(player, openSignObject);
}, 1);
}
{
TinyProtocol.instance.addFilter(updateSign, (player, o) -> {
Bukkit.getScheduler().runTask(BauSystem.getInstance(), () -> {
String[] lines = stringFieldAccessor.get(o);
Block signLoc = (Block) at.invoke(null, getWorldHandle.invoke(player.getWorld()), getBlockPositionFieldAccessor.get(o));
if (!signLoc.getType().name().contains("SIGN"))
return;
org.bukkit.block.Sign sign = ((Sign) signLoc.getState());
for (int i = 0; i < lines.length; i++) {
sign.setLine(i, ChatColor.translateAlternateColorCodes('&', lines[i]));
}
sign.update();
});
return o;
});
}
}
@@ -1,189 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.features.worldedit;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.region.Region;
import de.steamwar.bausystem.utils.FlatteningWrapper;
import de.steamwar.core.SWPlayer;
import de.steamwar.core.WorldEditRenderer;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MinVersion;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
@Linked
@MinVersion(20)
public class SelectAdjacent implements Listener {
private Vector[] FACES = {
new Vector(1, 0, 0),
new Vector(-1, 0, 0),
new Vector(0, 1, 0),
new Vector(0, -1, 0),
new Vector(0, 0, 1),
new Vector(0, 0, -1),
new Vector(1, 1, 0),
new Vector(1, -1, 0),
new Vector(1, 0, 1),
new Vector(1, 0, -1),
new Vector(-1, 1, 0),
new Vector(-1, -1, 0),
new Vector(-1, 0, 1),
new Vector(-1, 0, -1),
new Vector(0, 1, 1),
new Vector(0, 1, -1),
new Vector(0, -1, 1),
new Vector(0, -1, -1),
};
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
if (!event.hasItem()) return;
if (event.getItem().getType() != Material.WOODEN_AXE) return;
if (!event.getPlayer().isSneaking()) return;
if (event.getAction() != Action.LEFT_CLICK_BLOCK) return;
Material material = event.getPlayer().getInventory().getItemInOffHand().getType();
Selector selector;
if (material.isAir()) {
selector = new Selector(event.getClickedBlock(), event.getPlayer(), __ -> true);
} else {
selector = new Selector(event.getClickedBlock(), event.getPlayer(), type -> type == material);
}
SWPlayer.of(event.getPlayer()).setComponent(selector);
}
private class Selector implements SWPlayer.Component {
private static final int MAX_BLOCKS = 500_000;
private int minX;
private int minY;
private int minZ;
private int maxX;
private int maxY;
private int maxZ;
private BukkitTask bukkitTask;
private Predicate<Material> predicate;
private Set<Location> seen = new HashSet<>();
private Set<Location> toCalc = new HashSet<>();
private Region.Area area;
public Selector(Block block, Player player, Predicate<Material> predicate) {
this.predicate = predicate;
toCalc.add(block.getLocation());
minX = block.getX();
minY = block.getY();
minZ = block.getZ();
maxX = block.getX();
maxY = block.getY();
maxZ = block.getZ();
Region region = Region.getRegion(block.getLocation());
area = Region.Area.EMPTY;
if (region.getBuildArea().inRegion(block.getLocation(), true)) {
area = region.getBuildArea();
} else if (region.getTestblockArea().inRegion(block.getLocation(), true)) {
area = region.getTestblockArea();
} else if (region.getArea().inRegion(block.getLocation(), true)) {
area = region.getArea();
}
bukkitTask = Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> {
run();
long volume = (long)(maxX - minX + 1) * (long)(maxY - minY + 1) * (long)(maxZ - minZ + 1);
player.sendTitle("", "§e" + volume + " §7Blocks", 0, 5, 0);
Point minPoint = new Point(minX, minY, minZ);
Point maxPoint = new Point(maxX, maxY, maxZ);
FlatteningWrapper.impl.setSelection(player, minPoint, maxPoint);
WorldEditRenderer.renderPlayer(player);
// boolean finished = toCalc.stream().allMatch(location -> {
// return location.getBlockX() >= minX && location.getBlockY() >= minY && location.getBlockZ() >= minZ &&
// location.getBlockX() <= maxX && location.getBlockY() <= maxY && location.getBlockZ() <= maxZ;
// });
if (toCalc.isEmpty() || seen.size() > MAX_BLOCKS) {
bukkitTask.cancel();
player.sendTitle("§aDone", "§e" + volume + " §7Blocks", 0, 20, 5);
SWPlayer.of(player).removeComponent(Selector.class);
}
}, 1, 1);
}
private void cancel() {
bukkitTask.cancel();
}
private void run() {
Set<Location> current = toCalc;
toCalc = new HashSet<>();
for (Location location : current) {
Block block = location.getBlock();
if (block.isEmpty() || block.isLiquid()) continue;
if (!predicate.test(block.getType())) continue;
seen.add(location);
if (!area.inRegion(block.getLocation(), true)) continue;
minX = Math.min(minX, location.getBlockX());
maxX = Math.max(maxX, location.getBlockX());
minY = Math.min(minY, location.getBlockY());
maxY = Math.max(maxY, location.getBlockY());
minZ = Math.min(minZ, location.getBlockZ());
maxZ = Math.max(maxZ, location.getBlockZ());
for (Vector face : FACES) {
Block next = block.getRelative(face.getBlockX(), face.getBlockY(), face.getBlockZ());
if (next.isEmpty() || next.isLiquid()) continue;
if (!predicate.test(next.getType())) continue;
Location loc = next.getLocation();
if (seen.contains(loc)) continue;
toCalc.add(loc);
}
}
}
@Override
public void onUnmount(SWPlayer player) {
cancel();
}
}
}
@@ -20,7 +20,6 @@
package de.steamwar.bausystem.features.xray;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.features.techhider.TechHiderCommand;
import de.steamwar.bausystem.region.Region;
@@ -32,6 +31,7 @@ import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
import de.steamwar.techhider.TechHider;
import net.md_5.bungee.api.ChatMessageType;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
@@ -95,9 +95,6 @@ public class XrayCommand extends SWCommand implements Listener, ScoreboardElemen
});
}
private static final Class<?> position = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos");
private static final Class<?> positionLook = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot");
{
BiFunction<Player, Object, Object> positionSetter = (player, o) -> {
Region region = Region.getRegion(player.getLocation());
@@ -110,8 +107,8 @@ public class XrayCommand extends SWCommand implements Listener, ScoreboardElemen
return o;
};
TinyProtocol.instance.addFilter(position, positionSetter);
TinyProtocol.instance.addFilter(positionLook, positionSetter);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.Pos.class, positionSetter);
TinyProtocol.instance.addFilter(ServerboundMovePlayerPacket.PosRot.class, positionSetter);
}
@EventHandler
@@ -20,34 +20,211 @@
package de.steamwar.bausystem.utils;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import de.steamwar.bausystem.BauSystem;
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import com.sk89q.worldedit.function.mask.Mask;
import com.sk89q.worldedit.function.mask.Mask2D;
import com.sk89q.worldedit.function.operation.ForwardExtentCopy;
import com.sk89q.worldedit.function.operation.Operations;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.transform.AffineTransform;
import com.sk89q.worldedit.regions.CuboidRegion;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
import com.sk89q.worldedit.session.ClipboardHolder;
import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockTypes;
import de.steamwar.bausystem.region.Point;
import de.steamwar.core.VersionDependent;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Waterlogged;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Vector;
import javax.annotation.Nullable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiPredicate;
import java.util.logging.Level;
public interface FlatteningWrapper {
FlatteningWrapper impl = VersionDependent.getVersionImpl(BauSystem.getInstance());
public class FlatteningWrapper {
public static final FlatteningWrapper impl = new FlatteningWrapper();
boolean isNoBook(ItemStack item);
public boolean isNoBook(ItemStack item) {
return item.getType() != Material.WRITABLE_BOOK && item.getType() != Material.WRITTEN_BOOK;
}
boolean isUnpusheable(Material material);
boolean isBreakingOnPush(Material material);
private static final Set<Material> unpushable = new HashSet<>(Arrays.asList(Material.BARRIER, Material.BEACON, Material.COMMAND_BLOCK, Material.CHAIN_COMMAND_BLOCK, Material.REPEATING_COMMAND_BLOCK, Material.ENCHANTING_TABLE, Material.END_GATEWAY, Material.END_PORTAL, Material.ENDER_CHEST, Material.GRINDSTONE, Material.JIGSAW, Material.JUKEBOX, Material.NETHER_PORTAL, Material.OBSIDIAN, Material.STRUCTURE_VOID, Material.BARREL, Material.BEEHIVE, Material.BEE_NEST, Material.BLAST_FURNACE, Material.BREWING_STAND, Material.CHEST, Material.DAYLIGHT_DETECTOR, Material.DISPENSER, Material.DROPPER, Material.FURNACE, Material.HOPPER, Material.LECTERN, Material.SMOKER, Material.TRAPPED_CHEST));
boolean isWorldEditCommand(String command);
void setSelection(Player p, Point minPoint, Point maxPoint);
// TODO: FLOWER
private static final Set<Material> breaking = new HashSet<>(Arrays.asList(Material.BAMBOO, Material.CACTUS, Material.CAKE, Material.CARVED_PUMPKIN, Material.CHORUS_FLOWER, Material.CHORUS_PLANT, Material.COBWEB, Material.COCOA, Material.DRAGON_EGG, Material.FIRE, Material.FLOWER_POT, Material.JACK_O_LANTERN, Material.LADDER, Material.LAVA, Material.LAVA, Material.LEVER, Material.LILY_PAD, Material.MELON, Material.NETHER_WART, Material.PUMPKIN, Material.COMPARATOR, Material.REDSTONE_WIRE, Material.REPEATER, Material.TORCH, Material.STRUCTURE_VOID, Material.SCAFFOLDING, Material.SEA_PICKLE, Material.SNOW, Material.SUGAR_CANE, Material.TORCH, Material.TRIPWIRE, Material.TRIPWIRE_HOOK, Material.TURTLE_EGG, Material.VINE, Material.WATER, Material.WHEAT));
Clipboard loadSchematic(File file);
EditSession paste(PasteBuilder pasteBuilder);
public boolean isUnpusheable(Material material) {
if (unpushable.contains(material)) {
return true;
}
String name = material.name();
return name.contains("BANNER") || name.contains("SIGN");
}
Clipboard copy(Point minPoint, Point maxPoint, Point copyPoint);
boolean backup(Point minPoint, Point maxPoint, File file);
public boolean isBreakingOnPush(Material material) {
if (breaking.contains(material)) {
return true;
}
String name = material.name();
return name.contains("BED") || name.contains("BUTTON") || name.contains("CARPET") || (name.contains("DOOR") && !name.contains("TRAPDOOR")) || name.contains("HEAD") || name.contains("LEAVES") || name.contains("MUSHROOM") || name.contains("PRESSURE_PLATE") || name.contains("SHULKER_BOX");
}
boolean inWater(World world, Vector tntPosition);
public boolean isWorldEditCommand(String command) {
if (command.startsWith("/")) {
command = command.replaceFirst("/", "");
}
command = command.toLowerCase();
return WorldEdit.getInstance().getPlatformManager().getPlatformCommandManager().getCommandManager().containsCommand(command);
}
private static final WorldEditPlugin WORLDEDIT_PLUGIN = Objects.requireNonNull((WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit"));
private static final BukkitWorld BUKKITWORLD = new BukkitWorld(Bukkit.getWorlds().get(0));
public void setSelection(Player p, Point minPoint, Point maxPoint) {
WORLDEDIT_PLUGIN.getSession(p).setRegionSelector(BUKKITWORLD, new CuboidRegionSelector(BUKKITWORLD, minPoint.toBlockVector3(), maxPoint.toBlockVector3()));
}
public Clipboard loadSchematic(File file) {
Clipboard clipboard;
try (ClipboardReader reader = Objects.requireNonNull(ClipboardFormats.findByFile(file)).getReader(new FileInputStream(file))) {
clipboard = reader.read();
} catch (NullPointerException | IOException e) {
throw new SecurityException("Bausystem schematic not found", e);
}
return clipboard;
}
public EditSession paste(PasteBuilder pasteBuilder) {
try (EditSession e = WorldEdit.getInstance().getEditSessionFactory().getEditSession(new BukkitWorld(Bukkit.getWorlds().get(0)), -1)) {
Clipboard clipboard = pasteBuilder.getClipboard();
if (!pasteBuilder.getMappers().isEmpty()) {
BlockVector3 minimum = clipboard.getRegion().getMinimumPoint();
for (int x = 0; x < clipboard.getDimensions().getX(); x++) {
for (int y = 0; y < clipboard.getDimensions().getY(); y++) {
for (int z = 0; z < clipboard.getDimensions().getZ(); z++) {
BlockVector3 pos = minimum.add(x, y, z);
pasteBuilder.getMappers().forEach(mapper -> mapper.accept(clipboard, pos));
}
}
}
}
AtomicReference<BlockVector3> pastePoint = new AtomicReference<>();
if (!pasteBuilder.getPredicates().isEmpty()) {
e.setMask(new Mask() {
@Override
public boolean test(BlockVector3 blockVector3) {
BaseBlock block = clipboard.getFullBlock(blockVector3.subtract(pastePoint.get()).add(clipboard.getRegion().getMinimumPoint()));
String blockName = block.getBlockType().toString().toLowerCase();
for (BiPredicate<BaseBlock, String> predicate : pasteBuilder.getPredicates()) {
if (!predicate.test(block, blockName)) return false;
}
return true;
}
public Mask copy() {
return this;
}
@Nullable
@Override
public Mask2D toMask2D() {
return null;
}
});
}
ClipboardHolder ch = new ClipboardHolder(clipboard);
BlockVector3 dimensions = clipboard.getDimensions();
BlockVector3 v = BlockVector3.at(pasteBuilder.getPastPoint().getX(), pasteBuilder.getPastPoint().getY(), pasteBuilder.getPastPoint().getZ());
BlockVector3 offset = clipboard.getRegion().getMinimumPoint().subtract(clipboard.getOrigin());
if (pasteBuilder.isRotate()) {
ch.setTransform(new AffineTransform().rotateY(180));
v = v.add(dimensions.getX() / 2, 0, dimensions.getZ() / 2).subtract(offset.multiply(-1, 1, -1)).subtract(0, 0, 1);
} else {
v = v.subtract(dimensions.getX() / 2, 0, dimensions.getZ() / 2).subtract(offset);
}
pastePoint.set(v);
if (pasteBuilder.isReset()) {
e.setBlocks((Region) new CuboidRegion(pasteBuilder.getMinPoint().toBlockVector3(), pasteBuilder.getMaxPoint().toBlockVector3()), Objects.requireNonNull(BlockTypes.AIR).getDefaultState().toBaseBlock());
if (pasteBuilder.getWaterLevel() != 0) {
e.setBlocks((Region) new CuboidRegion(pasteBuilder.getMinPoint().toBlockVector3(), pasteBuilder.getMaxPoint().toBlockVector3().withY(pasteBuilder.getWaterLevel())), Objects.requireNonNull(BlockTypes.WATER).getDefaultState().toBaseBlock());
}
}
Operations.completeBlindly(ch.createPaste(e).to(v).ignoreAirBlocks(pasteBuilder.isIgnoreAir()).build());
return e;
} catch (WorldEditException e) {
throw new SecurityException(e.getMessage(), e);
}
}
public Clipboard copy(Point minPoint, Point maxPoint, Point copyPoint) {
BukkitWorld bukkitWorld = new BukkitWorld(Bukkit.getWorlds().get(0));
CuboidRegion region = new CuboidRegion(bukkitWorld, minPoint.toBlockVector3(), maxPoint.toBlockVector3());
BlockArrayClipboard clipboard = new BlockArrayClipboard(region);
try (EditSession e = WorldEdit.getInstance().getEditSessionFactory().getEditSession(bukkitWorld, -1)) {
ForwardExtentCopy copy = new ForwardExtentCopy(
e, region, clipboard, region.getMinimumPoint()
);
copy.setCopyingEntities(false);
copy.setCopyingBiomes(false);
Operations.complete(copy);
clipboard.setOrigin(copyPoint.toBlockVector3());
return clipboard;
} catch (WorldEditException e) {
Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
return null;
}
}
public boolean backup(Point minPoint, Point maxPoint, File file) {
Clipboard clipboard = copy(minPoint, maxPoint, minPoint);
try (ClipboardWriter writer = BuiltInClipboardFormat.SPONGE_SCHEMATIC.getWriter(new FileOutputStream(file))) {
writer.write(clipboard);
return true;
} catch (IOException e) {
Bukkit.getLogger().log(Level.SEVERE, e.getMessage(), e);
return false;
}
}
public boolean inWater(org.bukkit.World world, Vector tntPosition) {
Block block = world.getBlockAt(tntPosition.getBlockX(), tntPosition.getBlockY(), tntPosition.getBlockZ());
if (block.getType() == Material.WATER)
return true;
BlockData data = block.getBlockData();
if (!(data instanceof Waterlogged))
return false;
return ((Waterlogged) data).isWaterlogged();
}
}
@@ -19,25 +19,99 @@
package de.steamwar.bausystem.utils;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.core.VersionDependent;
import de.steamwar.Reflection;
import io.papermc.paper.datacomponent.DataComponentTypes;
import io.papermc.paper.datacomponent.item.ItemContainerContents;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.world.entity.player.Abilities;
import net.minecraft.world.level.GameType;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public interface NMSWrapper {
NMSWrapper impl = VersionDependent.getVersionImpl(BauSystem.getInstance());
import java.util.List;
import java.util.Optional;
void setInternalGameMode(Player player, GameMode gameMode);
void setSlotToItemStack(Player player, Object o);
public class NMSWrapper {
public static final NMSWrapper impl = new NMSWrapper();
void setGameStateChangeReason(Object packet);
void setPlayerBuildAbilities(Player player);
private static final Reflection.Field<GameType> playerGameMode = Reflection.getField(ServerPlayerGameMode.class, GameType.class, 0);
Material pathMaterial();
public void setInternalGameMode(Player player, GameMode gameMode) {
playerGameMode.set(((CraftPlayer) player).getHandle().gameMode, GameType.byId(gameMode.getValue()));
}
boolean checkItemStack(ItemStack item);
public void setSlotToItemStack(Player player, Object o) {
ClientboundContainerSetSlotPacket packetPlayInSetCreativeSlot = (ClientboundContainerSetSlotPacket) o;
int index = packetPlayInSetCreativeSlot.getSlot();
if (index >= 36 && index <= 44) {
index -= 36;
} else if (index > 44) {
index -= 5;
} else if (index <= 8) {
index = index - 8 + 36;
}
player.getInventory().setItem(index, CraftItemStack.asBukkitCopy(packetPlayInSetCreativeSlot.getItem()));
if (index < 9) player.getInventory().setHeldItemSlot(index);
player.updateInventory();
}
Object resetExplosionKnockback(Object packet);
public void setPlayerBuildAbilities(Player player) {
Abilities abilities = (((CraftPlayer) player).getHandle()).getAbilities();
abilities.mayBuild = true;
abilities.mayfly = true;
}
public Material pathMaterial() {
return Material.DIRT_PATH;
}
private static final int threshold = 2048;
public boolean checkItemStack(ItemStack item) {
ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER);
if (data == null) {
return false;
}
return drillDown(data.contents(), 0, 0) > threshold;
}
private int drillDown(List<ItemStack> items, int layer, int start) {
if (layer > 2) return start + threshold;
int invalid = start;
for (int i = start; i < items.size(); i++) {
ItemStack item = items.get(i);
if (item.isEmpty()) continue;
invalid += item.getAmount();
ItemContainerContents data = item.getData(DataComponentTypes.CONTAINER);
if (data == null) {
continue;
}
List<ItemStack> subItems = data.contents();
if (subItems.size() > 1) {
invalid = drillDown(subItems, layer + 1, invalid);
}
}
return invalid;
}
public Object resetExplosionKnockback(Object packet) {
ClientboundExplodePacket explosion = (ClientboundExplodePacket) packet;
return new ClientboundExplodePacket(
explosion.center(),
Optional.empty(),
explosion.explosionParticle(),
explosion.explosionSound()
);
}
}
@@ -23,11 +23,15 @@ import de.steamwar.Reflection;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import net.minecraft.core.BlockPos;
import org.bukkit.*;
import org.bukkit.block.*;
import org.bukkit.block.Skull;
import org.bukkit.block.data.*;
import org.bukkit.block.data.type.Hopper;
import org.bukkit.block.data.type.*;
import org.bukkit.block.data.type.Hopper;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.CraftBlockState;
import org.bukkit.entity.Player;
import org.bukkit.event.block.BlockCanBuildEvent;
import org.bukkit.event.block.BlockPlaceEvent;
@@ -82,27 +86,23 @@ public class PlaceItemUtils {
.collect(Collectors.toSet());
}
private static final Class<?> blockPosition = Reflection.getClass("net.minecraft.core.BlockPos");
private static final Reflection.Constructor blockPositionConstructor = Reflection.getConstructor(blockPosition, int.class, int.class, int.class);
private static final Class<?> craftBlock = Reflection.getClass("org.bukkit.craftbukkit.block.CraftBlockState");
private static final Class<?> craftWorld = Reflection.getClass("org.bukkit.craftbukkit.CraftWorld");
private static final Reflection.Field<?> positionAccessor = Reflection.getField(craftBlock, blockPosition, 0);
private static final Reflection.Field<?> worldAccessor = Reflection.getField(craftBlock, craftWorld, 0);
private static final Reflection.Field<?> positionAccessor = Reflection.getField(CraftBlockState.class, BlockPos.class, 0);
private static final Reflection.Field<?> worldAccessor = Reflection.getField(CraftBlockState.class, CraftWorld.class, 0);
/**
* Attempt to place an {@link ItemStack} the {@link Player} is holding against a {@link Block} inside the World.
* This can be easily used inside the {@link org.bukkit.event.player.PlayerInteractEvent} to mimik placing a
* block without any minecraft related GUI's etc. executing.
*
* @param player the Player placing the block
* @param itemStack the ItemStack to be placed
* @param against the Block at which the player aims (does not need to be in range of the player)
* @param againstSide the BlockFace the player aims
* @param hand the Hand the player is using
* @param force allow illegal states to be created by placing the block
* @param player the Player placing the block
* @param itemStack the ItemStack to be placed
* @param against the Block at which the player aims (does not need to be in range of the player)
* @param againstSide the BlockFace the player aims
* @param hand the Hand the player is using
* @param force allow illegal states to be created by placing the block
* @param applyPhysics apply physics while placing the block
* @param rotateAway rotate everything in the opposite direction, so a block facing the Player will face away, and the other way round
* @param playSound enables sound of placing
* @param rotateAway rotate everything in the opposite direction, so a block facing the Player will face away, and the other way round
* @param playSound enables sound of placing
*/
public PlaceItemResult placeItem(Player player, ItemStack itemStack, Block against, BlockFace againstSide, EquipmentSlot hand, boolean force, boolean applyPhysics, boolean rotateAway, boolean playSound) {
// If the ItemStack is null or air we cannot place it
@@ -288,7 +288,7 @@ public class PlaceItemUtils {
} else {
// If a BlockState is present set the Position and World to the Block you want to place
Location blockLocation = block.getLocation();
positionAccessor.set(blockState, blockPositionConstructor.invoke(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ()));
positionAccessor.set(blockState, new BlockPos(blockLocation.getBlockX(), blockLocation.getBlockY(), blockLocation.getBlockZ()));
worldAccessor.set(blockState, blockLocation.getWorld());
}
@@ -300,7 +300,7 @@ public class PlaceItemUtils {
Location blockmin = block.getLocation();
Location blockmax = block.getLocation().add(1.0, 1.0, 1.0);
if (
!(max.getX() <= blockmin.getX() || min.getX() >= blockmax.getX() ||
!(max.getX() <= blockmin.getX() || min.getX() >= blockmax.getX() ||
max.getY() <= blockmin.getY() || min.getY() >= blockmax.getY() ||
max.getZ() <= blockmin.getZ() || min.getZ() >= blockmax.getZ())
) {
@@ -369,8 +369,8 @@ public class PlaceItemUtils {
return usedForcePlace.get() ? PlaceItemResult.SUCCESS_FORCE : PlaceItemResult.SUCCESS;
}
public BlockFace[] axis = { BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST };
public BlockFace[] radial = { BlockFace.NORTH, BlockFace.NORTH_EAST, BlockFace.EAST, BlockFace.SOUTH_EAST, BlockFace.SOUTH, BlockFace.SOUTH_WEST, BlockFace.WEST, BlockFace.NORTH_WEST };
public BlockFace[] axis = {BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST};
public BlockFace[] radial = {BlockFace.NORTH, BlockFace.NORTH_EAST, BlockFace.EAST, BlockFace.SOUTH_EAST, BlockFace.SOUTH, BlockFace.SOUTH_WEST, BlockFace.WEST, BlockFace.NORTH_WEST};
public BlockFace yawToFace(float yaw) {
return radial[Math.round(yaw / 45f) & 0x7];
@@ -19,16 +19,32 @@
package de.steamwar.bausystem.utils;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.core.VersionDependent;
import lombok.experimental.UtilityClass;
import org.bukkit.Material;
import org.bukkit.block.data.BlockData;
import java.util.HashMap;
import java.util.Map;
public interface PlaceItemWrapper {
Map<Material, Material> ITEM_MATERIAL_TO_BLOCK_MATERIAL = new HashMap<>();
Map<Material, Material> BLOCK_MATERIAL_TO_WALL_BLOCK_MATERIAL = new HashMap<>();
@UtilityClass
public class PlaceItemWrapper {
public static final Map<Material, Material> ITEM_MATERIAL_TO_BLOCK_MATERIAL = new HashMap<>();
public static final Map<Material, Material> BLOCK_MATERIAL_TO_WALL_BLOCK_MATERIAL = new HashMap<>();
PlaceItemWrapper impl = VersionDependent.getVersionImpl(BauSystem.getInstance());
static {
for (Material material : Material.values()) {
if (!material.isBlock()) continue;
if (material.isLegacy()) continue;
BlockData blockData = material.createBlockData();
Material placementMaterial = blockData.getPlacementMaterial();
if (material == placementMaterial) continue;
if (placementMaterial == Material.AIR) continue;
if (placementMaterial.isItem() && !placementMaterial.isBlock()) {
ITEM_MATERIAL_TO_BLOCK_MATERIAL.put(placementMaterial, material);
}
if (material.name().contains("WALL")) {
BLOCK_MATERIAL_TO_WALL_BLOCK_MATERIAL.put(placementMaterial, material);
}
}
}
}
@@ -20,19 +20,33 @@
package de.steamwar.bausystem.utils;
import de.steamwar.Reflection;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.core.BountifulWrapper;
import de.steamwar.core.VersionDependent;
import de.steamwar.entity.REntity;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.server.level.ServerPlayer;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
public interface PlayerMovementWrapper {
public class PlayerMovementWrapper {
Class<?> teleportPacket = REntity.teleportPacket;
Reflection.Field<Integer> teleportEntity = REntity.teleportEntity;
BountifulWrapper.PositionSetter teleportPosition = REntity.teleportPosition;
PlayerMovementWrapper impl = VersionDependent.getVersionImpl(BauSystem.getInstance());
public static final PlayerMovementWrapper impl = new PlayerMovementWrapper();
void setPosition(Player player, Object object);
Object convertToOut(Player player, Object object);
public void setPosition(Player player, Object object) {
ServerboundMovePlayerPacket packetPlayInFlying = ((ServerboundMovePlayerPacket) object);
ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
if (packetPlayInFlying.hasPos) {
serverPlayer.setPosRaw(packetPlayInFlying.x, packetPlayInFlying.y, packetPlayInFlying.z);
}
if (packetPlayInFlying.hasRot) {
serverPlayer.setXRot(packetPlayInFlying.xRot);
serverPlayer.setYRot(packetPlayInFlying.yRot);
}
}
public Object convertToOut(Player player, Object object) {
return object;
}
}
@@ -1,31 +0,0 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 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.bausystem.utils;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.core.VersionDependent;
public interface TickListener {
TickListener impl = VersionDependent.getVersionImpl(BauSystem.getInstance());
default void init() {
}
}
@@ -19,28 +19,119 @@
package de.steamwar.bausystem.utils;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.core.VersionDependent;
import net.minecraft.network.protocol.game.ClientboundTickingStatePacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerTickRateManager;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
public interface TickManager extends Listener {
TickManager impl = VersionDependent.getVersionImpl(BauSystem.getInstance());
public class TickManager implements Listener {
public static final TickManager impl = new TickManager();
void setTickRate(float tickRate);
float getTickRate();
private static final ServerTickRateManager manager = MinecraftServer.getServer().tickRateManager();
private static final Reflection.Field<Long> remainingSprintTicks = Reflection.getField(ServerTickRateManager.class, long.class, 0);
boolean canFreeze();
void setFreeze(boolean freeze);
boolean isFrozen();
private boolean blockTpsPacket = true;
private int totalSteps;
void stepTicks(int ticks);
boolean isStepping();
private TickManager() {
TinyProtocol.instance.addFilter(ClientboundTickingStatePacket.class, this::blockPacket);
}
void sprintTicks(int ticks);
boolean isSprinting();
private Object blockPacket(Player player, Object packet) {
if (blockTpsPacket) {
return new ClientboundTickingStatePacket(20, manager.isFrozen());
} else {
return packet;
}
}
void setBlockTpsPacket(boolean block);
long getRemainingTicks();
long getDoneTicks();
long getTotalTicks();
public boolean canFreeze() {
return true;
}
public void setBlockTpsPacket(boolean block) {
blockTpsPacket = block;
if (blockTpsPacket) {
ClientboundTickingStatePacket packet = new ClientboundTickingStatePacket(20, manager.isFrozen());
Bukkit.getOnlinePlayers().forEach(player -> TinyProtocol.instance.sendPacket(player, packet));
} else {
ClientboundTickingStatePacket packet = new ClientboundTickingStatePacket(manager.tickrate(), manager.isFrozen());
Bukkit.getOnlinePlayers().forEach(player -> TinyProtocol.instance.sendPacket(player, packet));
}
}
public void setTickRate(float tickRate) {
if (isFrozen()) {
setFreeze(false);
}
manager.setTickRate(tickRate);
}
public boolean isFrozen() {
return manager.isFrozen();
}
public void setFreeze(boolean freeze) {
manager.setFrozen(freeze);
}
public void stepTicks(int ticks) {
if (manager.isSprinting()) {
manager.stopSprinting();
} else if (manager.isSteppingForward()) {
manager.stopStepping();
}
this.totalSteps = ticks;
manager.setFrozen(true);
manager.stepGameIfPaused(ticks);
manager.setFrozen(false);
Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), (bukkitTask) -> {
if (manager.isSteppingForward()) return;
manager.setFrozen(true);
bukkitTask.cancel();
}, 1, 1);
}
public void sprintTicks(int ticks) {
if (manager.isSteppingForward()) {
manager.stopStepping();
} else if (manager.isSprinting()) {
manager.stopSprinting();
}
this.totalSteps = ticks;
manager.requestGameToSprint(ticks, true);
}
public boolean isSprinting() {
return manager.isSprinting();
}
public boolean isStepping() {
return manager.isSteppingForward();
}
public float getTickRate() {
return manager.tickrate();
}
public long getRemainingTicks() {
if (isSprinting()) {
return remainingSprintTicks.get(manager);
} else {
return manager.frozenTicksToRun();
}
}
public long getDoneTicks() {
return totalSteps - getRemainingTicks();
}
public long getTotalTicks() {
return totalSteps;
}
}
@@ -19,7 +19,6 @@
package de.steamwar.bausystem.utils;
import de.steamwar.Reflection;
import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.IncompleteRegionException;
import com.sk89q.worldedit.LocalSession;
@@ -34,6 +33,7 @@ import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.regions.RegionSelector;
import com.sk89q.worldedit.regions.selector.limit.SelectorLimits;
import de.steamwar.Reflection;
import de.steamwar.bausystem.shared.Pair;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
@@ -26,8 +26,8 @@ tasks.compileJava {
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
dependencies {
@@ -19,8 +19,8 @@
package de.steamwar.bausystem.region.fixed.loader;
import de.steamwar.bausystem.region.fixed.FixedGlobalRegionData;
import de.steamwar.bausystem.region.fixed.FixedGlobalRegion;
import de.steamwar.bausystem.region.fixed.FixedGlobalRegionData;
import de.steamwar.bausystem.region.fixed.Prototype;
import de.steamwar.bausystem.worlddata.WorldData;
import lombok.experimental.UtilityClass;
-5
View File
@@ -29,11 +29,6 @@ tasks.build {
dependencies {
implementation(project(":BauSystem:BauSystem_RegionFixed"))
implementation(project(":BauSystem:BauSystem_Main"))
implementation(project(":BauSystem:BauSystem_15"))
implementation(project(":BauSystem:BauSystem_18"))
implementation(project(":BauSystem:BauSystem_19"))
implementation(project(":BauSystem:BauSystem_20"))
implementation(project(":BauSystem:BauSystem_21"))
}
tasks.register<DevServer>("DevBau20") {
+32
View File
@@ -0,0 +1,32 @@
plugins {
steamwar.kotlin
application
}
kotlin {
jvmToolchain(21)
}
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}
application {
mainClass.set("de.steamwar.MainKt")
applicationName = "sw"
}
dependencies {
implementation(project(":CommonCore:SQL"))
implementation("com.github.ajalt.clikt:clikt:5.0.3")
implementation("com.github.ajalt.mordant:mordant:3.0.2")
implementation(libs.logback)
implementation("org.mariadb.jdbc:mariadb-java-client:3.3.1")
implementation(libs.exposedCore)
implementation(libs.exposedDao)
implementation(libs.exposedJdbc)
implementation(libs.exposedTime)
}
+22
View File
@@ -0,0 +1,22 @@
package de.steamwar
import com.github.ajalt.clikt.core.main
import com.github.ajalt.clikt.core.subcommands
import de.steamwar.commands.SteamWar
import de.steamwar.commands.database.DatabaseCommand
import de.steamwar.commands.database.InfoCommand
import de.steamwar.commands.database.ResetCommand
import de.steamwar.commands.dev.DevCommand
import de.steamwar.commands.profiler.ProfilerCommand
import de.steamwar.commands.user.UserCommand
import de.steamwar.commands.user.UserInfoCommand
import de.steamwar.commands.user.UserSearchCommand
fun main(args: Array<String>) = SteamWar()
.subcommands(
DatabaseCommand().subcommands(InfoCommand(), ResetCommand()),
UserCommand().subcommands(UserInfoCommand(), UserSearchCommand()),
DevCommand(),
ProfilerCommand()
)
.main(args)
+10
View File
@@ -0,0 +1,10 @@
package de.steamwar.commands
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.mordant.rendering.TextStyles
class SteamWar: CliktCommand(name = "sw") {
override fun run() {
echo(TextStyles.bold("SteamWar-CLI"))
}
}
@@ -0,0 +1,22 @@
package de.steamwar.commands.database
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.CliktError
import com.github.ajalt.clikt.core.Context
import com.github.ajalt.clikt.core.findOrSetObject
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import de.steamwar.db.Database
class DatabaseCommand: CliktCommand(name = "db") {
val useProduction by option().flag()
val db by findOrSetObject { Database }
override fun help(context: Context): String = "Run database commands"
override fun run() {
if (!useProduction && db.database == "production") {
throw CliktError("You should not use the production database!")
}
}
}
+25
View File
@@ -0,0 +1,25 @@
package de.steamwar.commands.database
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.requireObject
import com.github.ajalt.mordant.table.table
import de.steamwar.db.Database
import de.steamwar.db.execute
import de.steamwar.db.useDb
class InfoCommand: CliktCommand() {
val db by requireObject<Database>()
override fun run() = useDb {
val tables = execute("SHOW TABLES") { it.getString(1) }
echo(
table {
header { row("Name") }
body {
tables.map { row(it) }
}
}
)
}
}
+33
View File
@@ -0,0 +1,33 @@
package de.steamwar.commands.database
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.CliktError
import com.github.ajalt.clikt.core.requireObject
import com.github.ajalt.mordant.rendering.TextColors
import com.github.ajalt.mordant.rendering.TextStyles
import de.steamwar.db.Database
import de.steamwar.db.execute
import de.steamwar.db.useDb
import java.io.File
class ResetCommand: CliktCommand() {
val db by requireObject<Database>()
override fun run() = useDb {
val schemaFile = File("/var/Schema.sql")
if (!schemaFile.exists()) {
throw CliktError("Schema file not found!")
}
val schema = schemaFile.readText()
val tables = execute("SHOW TABLES;") { it.getString(1) }
for (table in tables) {
execute("DROP TABLE IF EXISTS $table;") { }
}
execute(schema) { }
echo(TextColors.brightGreen(TextStyles.bold("Database reset!")))
}
}
+179
View File
@@ -0,0 +1,179 @@
package de.steamwar.commands.dev
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.CliktError
import com.github.ajalt.clikt.core.Context
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.help
import com.github.ajalt.clikt.parameters.arguments.multiple
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.defaultLazy
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.help
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.file
import com.github.ajalt.clikt.parameters.types.long
import com.github.ajalt.clikt.parameters.types.path
import com.sun.security.auth.module.UnixSystem
import java.io.File
import kotlin.io.path.absolute
import kotlin.io.path.absolutePathString
const val LOG4J_CONFIG = """<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" packages="com.mojang.util">
<Appenders>
<Console name="WINDOWS_COMPAT" target="SYSTEM_OUT"></Console>
<Queue name="TerminalConsole">
<PatternLayout pattern="[%d{HH:mm:ss} %level]: %msg{nolookups}%n" />
</Queue>
<RollingRandomAccessFile name="File" fileName="$\{'sys:logPath'}/latest.log" filePattern="$\{'sys:logPath'}/%d{yyyy.MM.dd}.log.gz">
<PatternLayout pattern="[%d{HH:mm:ss}] [%t/%level]: %msg{nolookups}%n" />
<Policies>
<TimeBasedTriggeringPolicy />
</Policies>
<DefaultRolloverStrategy max="7"/>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<Root level="info">
<filters>
<MarkerFilter marker="NETWORK_PACKETS" onMatch="DENY" onMismatch="NEUTRAL" />
</filters>
<AppenderRef ref="WINDOWS_COMPAT" level="info"/>
<AppenderRef ref="File"/>
<AppenderRef ref="TerminalConsole" level="info"/>
</Root>
</Loggers>
</Configuration>"""
class DevCommand : CliktCommand("dev") {
override fun help(context: Context): String = "Start a dev Server"
override val treatUnknownOptionsAsArgs = true
val server by argument().help("Server Template")
val port by option("--port").long().defaultLazy { UnixSystem().uid + 1010 }.help("Port for Server")
val world by option("--world", "-w").path(canBeFile = false).help("User World")
val plugins by option("--plugins", "-p").path(true, canBeFile = false).help("Plugin Dir")
val profile by option().flag().help("Add Profiling Arguments")
val forceUpgrade by option().flag().help("Force Upgrade")
val jar by option().file(true, canBeDir = false).help("Jar File")
val jvm by option().file(true, canBeDir = false).help("Java Executable")
val jvmArgs by argument().multiple()
override val printHelpOnEmptyArgs = true
val workingDir = File("").absoluteFile
val log4jConfig = File(workingDir, "log4j2.xml")
override fun run() {
val args = mutableListOf<String>()
val serverDirectory = File(workingDir, server)
val serverDir =
if (serverDirectory.exists() && serverDirectory.isDirectory) serverDirectory else File(workingDir, server)
if (isVelocity(server)) {
runServer(args, jvmArgs, listOf(jar?.absolutePath ?: File("/jar/Velocity.jar").absolutePath), serverDir)
} else {
setLogConfig(args)
val version = findVersion(server) ?: throw CliktError("Unknown Server Version")
val worldFile = world?.absolute()?.toFile() ?: File(serverDir, "devtempworld")
val jarFile = jar?.absolutePath ?: additionalVersions[server]?.let { supportedVersionJars[it] } ?: supportedVersionJars[version]
?: throw CliktError("Unknown Server Version")
if (!worldFile.exists()) {
val templateFile = File(serverDir, "Bauwelt")
if (!templateFile.exists()) {
throw CliktError("World Template not found!")
}
templateFile.copyRecursively(worldFile)
}
val devFile = File("/configs/DevServer/${System.getProperty("user.name")}.$port.$version")
if (System.getProperty("user.name") != "minecraft") {
devFile.createNewFile()
}
runServer(
args, jvmArgs, listOf(
jarFile,
*(if (forceUpgrade) arrayOf("-forceUpgrade") else arrayOf()),
"--port", port.toString(),
"--level-name", worldFile.name,
"--world-dir", workingDir.absolutePath,
"--nogui",
*(if (plugins != null) arrayOf("--plugins", plugins!!.absolutePathString()) else arrayOf())
), serverDir
)
try {
devFile.delete()
} catch (_: Exception) { /* ignored */ }
}
}
val jvmDefaultParams = arrayOf(
"-Xmx1G",
"-Xgc:excessiveGCratio=80",
"-Xsyslog:none",
"-Xtrace:none",
"-Xnoclassgc",
"-Xdisableexplicitgc",
"-XX:+AlwaysPreTouch",
"-XX:+CompactStrings",
"-XX:-HeapDumpOnOutOfMemory",
"-XX:+ExitOnOutOfMemoryError"
)
val jvmArgOverrides = arrayOf("--add-opens", "java.base/jdk.internal.misc=ALL-UNNAMED")
val supportedVersionJars = mapOf(
8 to "/jars/paper-1.8.8.jar",
9 to "/jars/spigot-1.9.4.jar",
10 to "/jars/paper-1.10.2.jar",
12 to "/jars/spigot-1.12.2.jar",
14 to "/jars/spigot-1.14.4.jar",
15 to "/jars/spigot-1.15.2.jar",
18 to "/jars/paper-1.18.2.jar",
19 to "/jars/paper-1.19.3.jar",
20 to "/jars/paper-1.20.1.jar",
21 to "/jars/paper-1.21.6.jar"
)
val additionalVersions = mapOf(
"Tutorial" to 15,
"Lobby" to 20
)
fun findVersion(server: String): Int? = server.dropWhile { !it.isDigit() }.toIntOrNull()
fun isJava8(server: String): Boolean = findVersion(server)?.let { it <= 10 } ?: false
fun isVelocity(server: String): Boolean = server.endsWith("Velocity")
fun setLogConfig(args: MutableList<String>) {
args += "-DlogPath=${workingDir.absolutePath}/logs"
args += "-Dlog4j.configurationFile=${log4jConfig.absolutePath}"
if (!log4jConfig.exists()) {
log4jConfig.writeText(LOG4J_CONFIG)
}
}
fun runServer(args: List<String>, jvmArgs: List<String>, cmd: List<String>, serverDir: File) {
val process = ProcessBuilder(
jvm?.absolutePath ?: if (isJava8(server)) "/usr/lib/jvm/openj9-8/bin/java" else "java",
*jvmArgs.toTypedArray(),
*args.toTypedArray(),
*jvmDefaultParams,
*(if (isJava8(server)) arrayOf() else jvmArgOverrides),
*(if (profile) arrayOf("-javaagent:/jars/LixfelsProfiler.jar=start") else arrayOf()),
"-Xshareclasses:nonfatal,name=$server",
"-jar",
*cmd.toTypedArray()
).directory(serverDir).inheritIO().start()
Runtime.getRuntime().addShutdownHook(Thread { if (process.isAlive) process.destroyForcibly() })
process.waitFor()
}
}
@@ -0,0 +1,42 @@
package de.steamwar.commands.profiler
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.Context
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.help
import com.github.ajalt.clikt.parameters.arguments.optional
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.int
const val SPARK = "/jars/spark.jar"
class ProfilerCommand: CliktCommand("profiler") {
val pid by argument().help("Process id").int().optional()
val port by option("--port", "-p").int().default(8543)
override fun run() {
if (pid != null) {
ProcessBuilder()
.command("java", "-jar", SPARK, pid.toString(), "port=$port")
.start()
.waitFor()
Thread.sleep(1000)
ProcessBuilder()
.command("ssh", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-p", port.toString(), "spark@localhost")
.inheritIO()
.start()
.waitFor()
} else {
ProcessBuilder()
.command("java", "-jar", SPARK)
.inheritIO()
.start()
.waitFor()
}
}
override fun help(context: Context): String = "Start a profiler"
}
+9
View File
@@ -0,0 +1,9 @@
package de.steamwar.commands.user
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.Context
class UserCommand: CliktCommand("user") {
override fun run() = Unit
override fun help(context: Context): String = "User related commands"
}
+65
View File
@@ -0,0 +1,65 @@
package de.steamwar.commands.user
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.CliktError
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.help
import com.github.ajalt.mordant.table.table
import de.steamwar.db.findUser
import de.steamwar.db.useDb
import de.steamwar.sql.Punishment
import de.steamwar.sql.SessionTable
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.Team
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.jdbc.selectAll
import java.time.Duration
class UserInfoCommand : CliktCommand("info") {
val userId by argument().help("Id, Name, UUID or DiscordId")
val user by lazy { findUser(userId) ?: throw CliktError("User not found") }
override val printHelpOnEmptyArgs = true
override fun run() = useDb {
val sessions =
SessionTable.selectAll().where { SessionTable.userId eq user.id.value }
.map { it[SessionTable.startTime] to it[SessionTable.endTime] }
val totalPlayed = sessions.sumOf { Duration.between(it.first, it.second).toMinutes() } / 60.0
val firstJoin = sessions.minByOrNull { it.first }?.first
val lastJoin = sessions.maxByOrNull { it.second }?.second
val punishments = Punishment.getAllPunishmentsOfPlayer(user.id.value)
echo(
table {
body {
row("Name", user.userName)
row("UUID", user.uuid)
row("Team", Team.byId(user.team).teamName)
row("Leader", user.leader)
row("Locale", user.locale)
row("Beigetreten am", firstJoin)
row("Zuletzt gesehen am", lastJoin)
row("Spielzeit", totalPlayed.toString() + "h")
row("Punishments", if (punishments.isEmpty()) "Keine" else table {
header { row("Typ", "Ersteller", "Von", "Bis", "Grund") }
body {
punishments.map {
row(
it.type,
SteamwarUser.byId(it.punisher)?.userName ?: it.punisher,
it.startTime.toString(),
if (it.perma) "Perma" else it.endTime.toString(),
it.reason
)
}
}
})
}
}
)
}
}
@@ -0,0 +1,42 @@
package de.steamwar.commands.user
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.Context
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.arguments.help
import com.github.ajalt.mordant.table.table
import de.steamwar.db.joinedOr
import de.steamwar.db.useDb
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.SteamwarUserTable
import de.steamwar.sql.Team
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.like
class UserSearchCommand : CliktCommand("search") {
val query by argument().help("Name, Id, UUID or DiscordId")
override val printHelpOnEmptyArgs = true
override fun help(context: Context): String = "Search for users"
override fun run() = useDb {
val users = SteamwarUser.find {
joinedOr(
SteamwarUserTable.username like "%$query%",
SteamwarUserTable.uuid like "%$query%",
query.toLongOrNull()?.let { SteamwarUserTable.discordId eq it },
query.toIntOrNull()?.let { SteamwarUserTable.id eq it }
)
}
val teams = mutableMapOf<Int, Team>()
echo(table {
header { row("Id", "Username", "UUID", "Team", "DiscordId") }
body {
users.map { row(it.id.value, it.userName, it.uuid, teams.computeIfAbsent(it.team) { teamId -> Team.byId(teamId) }.teamName, it.discordId) }
}
})
}
}
+84
View File
@@ -0,0 +1,84 @@
package de.steamwar.db
import com.github.ajalt.clikt.core.BaseCliktCommand
import com.github.ajalt.clikt.core.CliktError
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.SteamwarUserTable
import org.jetbrains.exposed.v1.core.Expression
import org.jetbrains.exposed.v1.core.Op
import org.jetbrains.exposed.v1.core.eq
import org.jetbrains.exposed.v1.core.or
import org.jetbrains.exposed.v1.jdbc.Database
import org.jetbrains.exposed.v1.jdbc.JdbcTransaction
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
import java.io.File
import java.sql.ResultSet
import java.util.Properties
object Database {
lateinit var host: String
lateinit var port: String
lateinit var database: String
lateinit var db: Database
fun ensureConnected() {
if (::db.isInitialized) {
return
}
val config = File(System.getProperty("user.home"), "mysql.properties")
if (!config.exists()) {
throw CliktError("Config file not found!")
}
val props = Properties();
props.load(config.inputStream())
host = props.getProperty("host")
port = props.getProperty("port")
database = props.getProperty("database")
val username = props.getProperty("user")
val password = props.getProperty("password")
val url = "jdbc:mariadb://$host:$port/$database"
db = Database.connect(url, driver = "org.mariadb.jdbc.Driver", user = username, password = password)
return
}
}
fun <T: BaseCliktCommand<T>> BaseCliktCommand<T>.findUser(query: String): SteamwarUser? = transaction {
SteamwarUser.find { joinedOr(query.toIntOrNull()?.let { SteamwarUserTable.id eq it }, (SteamwarUserTable.username eq query), SteamwarUserTable.uuid eq query, query.toLongOrNull()?.let { SteamwarUserTable.discordId eq it }) }
.firstOrNull()
?.let { return@transaction it }
}
fun joinedOr(vararg expressions: Expression<Boolean>?): Op<Boolean> =
expressions.filterNotNull().reduce { acc, expression -> acc or expression } as Op<Boolean>
fun <T> JdbcTransaction.execute(sql: String, transform: (ResultSet) -> T): List<T> {
val result = mutableListOf<T>()
exec(sql) { rs ->
while (rs.next()) {
result += transform(rs)
}
}
return result
}
fun <T> JdbcTransaction.executeSingle(sql: String, transform: (ResultSet) -> T): T? {
return execute(sql) { rs ->
if (!rs.next()) {
return@execute null
}
transform(rs)
}.single()
}
fun useDb(statement: JdbcTransaction.() -> Unit) {
de.steamwar.db.Database.ensureConnected()
transaction(de.steamwar.db.Database.db, statement)
}
+11
View File
@@ -0,0 +1,11 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="WARN">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
* Copyright (C) 2026 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
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
* Copyright (C) 2026 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
@@ -28,11 +28,11 @@ import org.jetbrains.exposed.v1.javatime.timestamp
import java.time.Instant
object AuditLogTable: IntIdTable("AuditLog", "AuditLogId") {
val time = timestamp("Time")
val server = varchar("ServerName", 255)
val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable()
val actor = reference("Actor", SteamwarUserTable)
val action = enumerationByName("ActionType", 255, AuditLog.Type::class)
val time = timestamp("Time").index()
val server = varchar("ServerName", 255).index()
val serverOwner = reference("ServerOwner", SteamwarUserTable).nullable().index()
val actor = reference("Actor", SteamwarUserTable).index()
val action = enumerationByName("ActionType", 255, AuditLog.Type::class).index()
val actionText = text("ActionText")
}
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
* Copyright (C) 2026 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
@@ -33,9 +33,13 @@ import java.sql.Timestamp
import java.time.Instant
object BannedUserIPsTable: CompositeIdTable("BannedUserIPs") {
val userId = reference("UserID", SteamwarUserTable)
val timestamp = timestamp("Timestamp")
val ip = varchar("IP", 45)
val userId = reference("UserID", SteamwarUserTable).index()
val timestamp = timestamp("Timestamp").index()
val ip = varchar("IP", 45).entityId()
init {
addIdColumn(userId)
}
override val primaryKey = PrimaryKey(userId, ip)
}
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
* Copyright (C) 2026 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
@@ -32,8 +32,8 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.util.*
object BauweltMemberTable: CompositeIdTable("BauweltMember") {
val bauweltId = reference("BauweltID", SteamwarUserTable)
val memberId = reference("MemberID", SteamwarUserTable)
val bauweltId = reference("BauweltID", SteamwarUserTable).index()
val memberId = reference("MemberID", SteamwarUserTable).index()
val build = bool("Build")
val worldEdit = bool("WorldEdit")
val world = bool("World")
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
* Copyright (C) 2026 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,6 +20,7 @@
package de.steamwar.sql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.ReferenceOption
import org.jetbrains.exposed.v1.core.SortOrder
import org.jetbrains.exposed.v1.core.and
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
@@ -34,19 +35,23 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
import java.sql.Timestamp
object CheckedSchematicTable: CompositeIdTable("CheckedSchematic") {
val nodeId = optReference("NodeId", SchematicNodeTable)
val nodeOwner = reference("NodeOwner", SteamwarUserTable)
val nodeName = varchar("NodeName", 64).entityId()
val validator = reference("Validator", SteamwarUserTable)
val startTime = timestamp("StartTime").entityId()
val nodeId = optReference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL, onUpdate = ReferenceOption.SET_NULL).index()
val nodeOwner = reference("NodeOwner", SteamwarUserTable).index()
val nodeName = varchar("NodeName", 64).entityId().index()
val validator = reference("Validator", SteamwarUserTable).index()
val startTime = timestamp("StartTime").entityId().index()
val endTime = timestamp("EndTime")
val declineReason = text("DeclineReason")
val seen = bool("Seen")
val seen = bool("Seen").index()
val nodeType = varchar("NodeType", 16)
init {
addIdColumn(nodeOwner)
addIdColumn(nodeName)
index(false, nodeOwner, endTime)
index(false, startTime, endTime, nodeName)
index(false, seen, nodeOwner, startTime)
}
}
+3 -3
View File
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
* Copyright (C) 2026 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
@@ -36,8 +36,8 @@ import java.time.Instant
object EventTable : IntIdTable("Event", "EventId") {
val name = varchar("EventName", 100).uniqueIndex()
val deadline = timestamp("Deadline")
val start = timestamp("Start")
val end = timestamp("End")
val start = timestamp("Start").index()
val end = timestamp("End").index()
val maxPlayers = integer("MaximumTeamMembers")
val schemType = varchar("SchemType", 16).nullable()
val publicsOnly = bool("PublicSchemsOnly")
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
* Copyright (C) 2026 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
@@ -33,17 +33,17 @@ import java.time.Instant
import java.util.*
object EventFightTable : IntIdTable("EventFight", "FightID") {
val eventId = reference("EventID", EventTable)
val startTime = timestamp("StartTime")
val eventId = reference("EventID", EventTable).index()
val startTime = timestamp("StartTime").index()
val gamemode = text("Spielmodus")
val map = text("Map")
val groupId = optReference("GroupId", EventGroupTable)
val teamBlue = reference("TeamBlue", TeamTable)
val teamRed = reference("TeamRed", TeamTable)
val groupId = optReference("GroupId", EventGroupTable).index()
val teamBlue = reference("TeamBlue", TeamTable).index()
val teamRed = reference("TeamRed", TeamTable).index()
val spectatePort = integer("SpectatePort").nullable()
val bestOf = integer("BestOf")
val ergebnis = integer("Ergebnis")
val fight = optReference("Fight", FightTable)
val fight = optReference("Fight", FightTable).index()
}
class EventFight(id: EntityID<Int>) : IntEntity(id), Comparable<EventFight> {
@@ -34,6 +34,10 @@ object EventGroupTable : IntIdTable("EventGroup", "Id") {
val pointsPerWin = integer("PointsPerWin").default(3)
val pointsPerLoss = integer("PointsPerLoss").default(0)
val pointsPerDraw = integer("PointsPerDraw").default(1)
init {
uniqueIndex(event, name)
}
}
class EventGroup(id: EntityID<Int>) : IntEntity(id) {
@@ -90,7 +94,7 @@ class EventGroup(id: EntityID<Int>) : IntEntity(id) {
set(value) {
groupPointsPerDraw = value
}
val dependents by lazy { EventRelation.getGroupRelations(this).toList() }
val dependents by lazy { EventRelation.getGroupRelations(this) }
val lastFight by lazy { Optional.ofNullable(fights.maxByOrNull { it.startTime }) }
fun getId() = id.value
@@ -29,7 +29,7 @@ import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.jdbc.select
object EventRelationTable : IntIdTable("EventRelation") {
val fightId = reference("FightId", EventFightTable)
val fightId = reference("FightId", EventFightTable).index()
val fightTeam = enumeration("FightTeam", EventRelation.FightTeam::class)
val fromType = enumeration("FromType", EventRelation.FromType::class)
val fromId = integer("FromId")
@@ -51,11 +51,11 @@ class EventRelation(id: EntityID<Int>) : IntEntity(id) {
@JvmStatic
fun getFightRelations(fight: EventFight) =
useDb { find { (EventRelationTable.fromId eq fight.id.value) and (EventRelationTable.fromType eq FromType.FIGHT) } }
useDb { find { (EventRelationTable.fromId eq fight.id.value) and (EventRelationTable.fromType eq FromType.FIGHT) }.toList() }
@JvmStatic
fun getGroupRelations(group: EventGroup) =
useDb { find { (EventRelationTable.fromId eq group.id.value) and (EventRelationTable.fromType eq FromType.GROUP) } }
useDb { find { (EventRelationTable.fromId eq group.id.value) and (EventRelationTable.fromType eq FromType.GROUP) }.toList() }
@JvmStatic
fun create(fight: EventFight, fightTeam: FightTeam, fromType: FromType, fromId: Int, fromPlace: Int) = useDb {
+5 -5
View File
@@ -34,14 +34,14 @@ import org.jetbrains.exposed.v1.jdbc.update
import java.sql.Timestamp
object FightTable : IntIdTable("Fight", "FightId") {
val gamemode = varchar("Gamemode", 30)
val gamemode = varchar("Gamemode", 30).index()
val server = text("Server")
val startTime = timestamp("StartTime")
val duration = integer("Duration")
val blueLeader = reference("BlueLeader", SteamwarUserTable)
val redLeader = reference("RedLeader", SteamwarUserTable)
val blueSchem = optReference("BlueSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL)
val redSchem = optReference("RedSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL)
val blueLeader = reference("BlueLeader", SteamwarUserTable).index()
val redLeader = reference("RedLeader", SteamwarUserTable).index()
val blueSchem = optReference("BlueSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL).index()
val redSchem = optReference("RedSchem", SchematicNodeTable, onDelete = ReferenceOption.SET_NULL).index()
val win = enumeration("Win", Fight.WinningTeam::class)
val winCondition = varchar("WinCondition", 100)
val replayAvailable = bool("ReplayAvailable")
@@ -30,7 +30,7 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
object FightPlayerTable : CompositeIdTable("FightPlayer") {
val fightId = reference("FightId", FightTable)
val userId = reference("UserId", SteamwarUserTable)
val userId = reference("UserId", SteamwarUserTable).index()
val team = integer("Team")
val kit = varchar("Kit", 64)
val kills = integer("Kills")
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2025 SteamWar.de-Serverteam
* Copyright (C) 2026 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
@@ -52,6 +52,10 @@ public final class GameModeConfig<M, W> {
private static final Map<String, GameModeConfig<?, String>> byGameName;
private static final Map<SchematicType, GameModeConfig<?, String>> bySchematicType;
public static <M> Collection<GameModeConfig<M, String>> getAll() {
return (Collection) byFileName.values();
}
public static <M> GameModeConfig<M, String> getByFileName(File file) {
return (GameModeConfig<M, String>) byFileName.get(file.getName());
}
@@ -87,8 +91,24 @@ public final class GameModeConfig<M, W> {
byFileName = new HashMap<>();
byGameName = new HashMap<>();
bySchematicType = new HashMap<>();
SchematicType.values();
DEFAULTS = SQLWrapper.impl.loadGameModeConfig(null);
init();
}
public static void init() {
byFileName.clear();
byGameName.clear();
bySchematicType.clear();
File folder = SQLWrapper.impl.getSchemTypesFolder();
if (!folder.exists()) return;
if (!folder.isDirectory()) return;
for (File file : Objects.requireNonNull(folder.listFiles())) {
if (!file.getName().endsWith(".yml")) continue;
if (file.getName().endsWith(".kits.yml")) continue;
SQLWrapper.impl.loadGameModeConfig(file);
}
byFileName.values().forEach(gameModeConfig -> {
List<SchematicType> subTypes = Collections.unmodifiableList(gameModeConfig.Schematic.SubTypesStrings.stream()
@@ -402,6 +422,13 @@ public final class GameModeConfig<M, W> {
*/
public final int WaterDepth;
/**
* If TNT should break blocks even underwater
*
* @implSpec {@code true} by default
*/
public final boolean WaterDamage;
/**
* The outer border of the arena, measured in blocks around the schematic areas
*/
@@ -433,6 +460,13 @@ public final class GameModeConfig<M, W> {
*/
public final boolean DisableSnowMelt;
/**
* Disable ice forming
*
* @implSpec {@code false} by default
*/
public final boolean DisableIceForm;
/**
* Allow leaving the arena area as spectator
*
@@ -457,11 +491,13 @@ public final class GameModeConfig<M, W> {
private ArenaConfig(YMLWrapper loader, SchematicConfig.SizeConfig Size, List<Integer> EnterStages) {
loaded = loader.canLoad();
WaterDepth = loader.getInt("WaterDepth", 0);
WaterDamage = loader.getBoolean("WaterDamage", true);
Schem2Border = new Schem2BorderConfig(loader.with("Schem2Border"));
SpawnOffset = new SpawnOffsetConfig(loader.with("SpawnOffset"), Size);
BorderFromSchematic = loader.getInt("BorderFromSchematic", 21);
GroundWalkable = loader.getBoolean("GroundWalkable", true);
DisableSnowMelt = loader.getBoolean("DisableSnowMelt", false);
DisableIceForm = loader.getBoolean("DisableIceForm", false);
Leaveable = loader.getBoolean("Leaveable", false);
AllowMissiles = loader.getBoolean("AllowMissiles", !EnterStages.isEmpty());
NoFloor = loader.getBoolean("NoFloor", false);
@@ -597,18 +633,18 @@ public final class GameModeConfig<M, W> {
public final boolean IgnorePublicOnly;
/**
* If obsidian and bedrock should be replaced during PRE_RUNNING
* Replacements that should be done during PRE_RUNNING with no block updates
*
* @implSpec {@code false} by default
* @implSpec {@code {}} by default
*/
public final boolean ReplaceObsidianBedrock;
public final Map<M, M> ReplacementsWithoutBlockUpdates;
/**
* If the replacement should happen with block updates
* Replacements that should be done during PRE_RUNNING with block updates
*
* @implSpec {@code false} by default
* @implSpec {@code {}} by default
*/
public final boolean ReplaceWithBlockupdates;
public final Map<M, M> ReplacementsWithBlockUpdates;
/**
* If the schematic perparation arena mode is time limited
@@ -641,7 +677,7 @@ public final class GameModeConfig<M, W> {
/**
* Maximal blast resistance for the design blocks
*
* @implSpec {@code Double.MAX_VALUE} by default
* @implSpec {@link SchematicConfig#MaxBlastResistance} by default
*/
public final double MaxDesignBlastResistance;
@@ -655,9 +691,9 @@ public final class GameModeConfig<M, W> {
loaded = loader.canLoad();
Size = new SizeConfig(loader.with("Size"));
Inset = new InsetConfig(loader.with("Inset"));
Type = loader.getSchematicType("Type", "Normal");
Type = null;
SubTypesStrings = loader.getStringList("SubTypes");
SubTypes = loader.getSchematicTypeList("SubTypes");
SubTypes = new ArrayList<>();
Shortcut = loader.getString("Shortcut", "");
Material = loader.getMaterial("Material", "STONE_BUTTON");
ManualCheck = loader.getBoolean("ManualCheck", true);
@@ -665,13 +701,11 @@ public final class GameModeConfig<M, W> {
PasteAligned = loader.getBoolean("PasteAligned", false);
OnlyPublicSchematics = loader.getBoolean("OnlyPublicSchematics", false);
IgnorePublicOnly = loader.getBoolean("IgnorePublicOnly", false);
ReplaceObsidianBedrock = loader.getBoolean("ReplaceObsidianBedrock", false);
ReplaceWithBlockupdates = loader.getBoolean("ReplaceWithBlockupdates", false);
UnlimitedPrepare = loader.getBoolean("UnlimitedPrepare", false);
MaxBlocks = loader.getInt("MaxBlocks", 0);
MaxDispenserItems = loader.getInt("MaxDispenserItems", 128);
MaxBlastResistance = loader.getDouble("MaxBlastResistance", Double.MAX_VALUE);
MaxDesignBlastResistance = loader.getDouble("MaxDesignBlastResistance", Double.MAX_VALUE);
MaxDesignBlastResistance = loader.getDouble("MaxDesignBlastResistance", MaxBlastResistance);
Map<Set<M>, Integer> Limited = new HashMap<>();
for (Map<?, ?> entry : loader.getMapList("Limited")) {
@@ -690,6 +724,9 @@ public final class GameModeConfig<M, W> {
Limited.put(Collections.singleton((M) material), 0);
});
this.Limited = Collections.unmodifiableMap(Limited);
this.ReplacementsWithoutBlockUpdates = loader.getMap("ReplacementsWithoutBlockUpdates", loader.materialMapper, loader.materialMapper);
this.ReplacementsWithBlockUpdates = loader.getMap("ReplacementsWithBlockUpdates", loader.materialMapper, loader.materialMapper);
}
@ToString
@@ -30,8 +30,8 @@ import org.jetbrains.exposed.v1.dao.CompositeEntityClass
import java.util.*
object IgnoreSystemTable: CompositeIdTable("IgnoredPlayers") {
val ignorer = reference("Ignorer", SteamwarUserTable)
val ignored = reference("Ignored", SteamwarUserTable)
val ignorer = reference("Ignorer", SteamwarUserTable).index()
val ignored = reference("Ignored", SteamwarUserTable).index()
override val primaryKey = PrimaryKey(ignorer, ignored)

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