Compare commits

..
Author SHA1 Message Date
Chaoscaot 07bf47481a Fix
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-12 20:52:36 +02:00
Chaoscaot 5305aaf669 Normalize block entity IDs and handle bundled packets
- Normalize hidden block entity identifiers across versions
- Add block break and tile entity handling for newer protocol versions
- Preserve packet filtering through bundle packets and fix chunk section copying
2026-05-11 23:40:36 +02:00
YoyoNow 1ea8dea381 Fix WhoisCommand.whois 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
Reviewed-on: SteamWar/SteamWar#332
Reviewed-by: D4rkr34lm <dark@steamwar.de>
2026-05-10 18:55:25 +02:00
Chaoscaot 6a843f4a71 Implement old bau behavior for WGS
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
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
Reviewed-on: SteamWar/SteamWar#262
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
2026-05-09 20:23:56 +02:00
Chaoscaot 42ab55d0f8 Merge branch 'main' into add-db-indexes
# Conflicts:
#	CommonCore/SQL/src/de/steamwar/sql/BannedUserIPs.kt
2026-05-09 20:22:15 +02:00
YoyoNow 8ade5180cb Fix FightSystem 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
Reviewed-on: SteamWar/SteamWar#322
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-05-04 18:31:25 +02:00
zOnlyKroks 79fa09e39b Chaos zufrieden stellen 2026-05-04 18:27:28 +02:00
Chaoscaot 4010c2125c Refactor lazy loading of dependents and relations to return lists
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-05-04 13:41:07 +02:00
zOnlyKroks 32de0077de [fightsystem]: Rework blacklist to whitelist 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
Reviewed-on: SteamWar/SteamWar#320
Reviewed-by: zOnlyKroks <zonlyknox@gmail.com>
2026-05-03 13:04:37 +02:00
zOnlyKroks 5b1ed644d1 [fightsystem]: Fix broken kit system 2026-05-03 12:52:23 +02:00
D4rkr34lm a41787d89d Add flag 2026-05-02 13:36:32 +02:00
Chaoscaot 8e392b56c3 Merge pull request 'fix: windcharge check' (#318) from fix/mssing-string-get into main
Reviewed-on: SteamWar/SteamWar#318
Reviewed-by: Chaoscaot <max@chaoscaot.de>
2026-05-01 12:30:42 +02:00
D4rkr34lm 42feadcd2d Add left out name get 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
Reviewed-on: SteamWar/SteamWar#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 2026-05-01 11:02:33 +02:00
D4rkr34lm b186228f4e Revert "swap out used api"
This reverts commit 5f38474809.
2026-05-01 10:59:23 +02:00
D4rkr34lm 5f38474809 swap out used api 2026-05-01 10:54:53 +02:00
Manuel Frohn 4f27320548 Implement hardcoded windcharge check 2026-04-30 14:44:54 +02:00
D4rkr34lm ba7bd1f1dd Configure V21 impl 2026-04-30 12:52:46 +02:00
YoyoNow fbe70e7ead Improve CheckCommand for WGS 2026-04-24 10:09:56 +02:00
YoyoNow 30a499be1d Hotfix CheckCommand
Remove uneeded stuff in SQLWrapper
2026-04-23 23:33:02 +02:00
Chaoscaot 86e212fe42 Fix Kits
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-04-23 18:23:13 +02:00
YoyoNow 4a646e6be0 Improve WindchargeStopper 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
Reviewed-on: SteamWar/SteamWar#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
Reviewed-on: SteamWar/SteamWar#271
2026-04-23 12:01:18 +02:00
YoyoNow 703639537d Fix CreateKitCommand 2026-04-23 11:59:28 +02:00
YoyoNow 9ac3bf6a6c Redesign GameModeConfig and SchematicType 2026-04-23 11:54:50 +02:00
YoyoNow 41ea6c9407 Fix ViewFlag.ADVANCED 2026-04-23 08:27:43 +02:00
YoyoNow 67e9a3544e Fix Tablist.disable removing gm knowledge 2026-04-20 13:45:16 +02:00
YoyoNow f69ae3e77b Fix BauLock 2026-04-20 13:31:45 +02:00
Chaoscaot 2208dcc0fb Fix Set Parent
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-04-15 18:20:07 +02:00
YoyoNow b466216b3a Fix BlockFormListener 2026-04-13 20:33:11 +02:00
YoyoNow 5a862b251b Add BlockFormListener 2026-04-13 20:29:47 +02:00
YoyoNow 60a82a685d Improve FightSystem REDUCED_DEBUG_INFO on Test Arena 2026-04-13 20:18:43 +02:00
YoyoNow 573b0c14ae Improve FightSystem REDUCED_DEBUG_INFO on Test Arena 2026-04-13 20:03:41 +02:00
YoyoNow 82abe7e20f Fix OutsideWincondition 2026-04-05 12:34:11 +02:00
YoyoNow 34da59714e Fix IngameListener and StartCommand 2026-04-05 12:30:55 +02:00
YoyoNow 97071165cd Fix IngameListener, OutsideWincondition, TowerGenerator 2026-04-05 12:08:29 +02:00
YoyoNow 634465fbf1 Fix BanListener inserting bedrock ips 2026-04-04 12:08:17 +02:00
YoyoNow 2ad8cc3f4a Merge branch 'RemoveUnusedSQL' 2026-04-02 09:01:20 +02:00
YoyoNow e190fe0858 Fix FreezeListener 2026-04-01 19:43:31 +02:00
YoyoNow 569d91a0d3 Remove SelectAdjacent as it annoys most players 2026-03-29 14:15:01 +02:00
YoyoNow 487a15849a Add supress warnings 2026-03-29 13:12:09 +02:00
YoyoNow e110033315 Fix Replays for 1.21 2026-03-29 13:05:41 +02:00
YoyoNow c0b192e2bf Fix WorldEditWrapper not loading schematics in 1.21 2026-03-29 12:53:55 +02:00
YoyoNow 612254296c Fix WorldEditWrapper21 2026-03-29 11:55:16 +02:00
Chaoscaot 1dbcb122c2 Change Loader to use FastSchematicReaderV3
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-03-28 22:30:01 +01:00
Chaoscaot f2ee9dbeb3 Fix Schematic Tabcomplete
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-03-28 22:24:07 +01:00
YoyoNow 404ab2abfb Update GDPRQuery 2026-03-25 07:46:38 +01:00
YoyoNow 59a927c33c Remove Team.address and Team.port
Remove PollAnswer and UserElo from GDPRQuery
2026-03-24 22:11:19 +01:00
Chaoscaot 6c062216a1 Remove usage of EffectiveSchematicNode
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-03-24 20:07:17 +01:00
YoyoNow 72d62dfbe5 Fix AutoCheckerResult ignoring Water and Lava correctly 2026-03-22 15:23:46 +01:00
YoyoNow 76ecaccc41 Hotfix AutoChecker15 for now 2026-03-21 17:45:26 +01:00
YoyoNow 9587b9e1fd Fix Laufbau double click 2026-03-21 09:46:20 +01:00
YoyoNow 14dc807fd9 Fix SoulSand in LaufbauCommand 2026-03-21 09:39:17 +01:00
YoyoNow 63ad85f727 Fix TickManager21.stepTicks 2026-03-21 09:37:05 +01:00
YoyoNow 72e88502d2 Fix TeamCommand /team event ... 2026-03-21 09:26:02 +01:00
YoyoNow 71767ef6d9 Fix TeamCommand /team event ... 2026-03-21 09:20:57 +01:00
YoyoNow 5e19629df5 Fix build 2026-03-15 12:54:43 +01:00
YoyoNow ca70c6685c Remove Lunar client support
They currently have a problem with their maven repository
2026-03-15 12:52:56 +01:00
YoyoNow f00bd153fe Add GameModeConfig#Schematic#ReplacementsWithoutBlockUpdates
Add GameModeConfig#Schematic#ReplacementsWithBlockUpdates
2026-03-15 12:49:13 +01:00
YoyoNow c1221e5cf5 Remove SWTSI (SteamWar Teamserver Integration) 2026-03-13 21:16:13 +01:00
YoyoNow 236944ff69 Remove useless System.out 2026-03-13 21:12:34 +01:00
YoyoNow ab85c72fe3 Fix DesignEndStone
Closes: #292
Closes: #288
2026-03-13 21:08:00 +01:00
YoyoNow a750185df0 Fix stop not working for DevServer starter 2026-03-02 12:10:18 +01:00
YoyoNow 008ff1091f Hotfix DevCommand 2026-03-02 11:59:52 +01:00
YoyoNow 5d24581038 Fix WaterRemover.handleEntityExplode 2026-03-01 21:47:22 +01:00
YoyoNow bce07a4ac8 Add GameModeConfig.ArenaConfig.WaterDamage for hard water damage by setting air or normal handling 2026-03-01 21:36:29 +01:00
Chaoscaot 30b7bbc283 Fix WebPW Command
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-02-09 09:28:14 +01:00
Chaoscaot 46a11af6ca Fix Ban Command
Signed-off-by: Chaoscaot <max@maxsp.de>
2026-02-02 19:12:34 +01:00
Chaoscaot 73f903fc23 Merge branch 'main' into add-db-indexes 2026-01-23 23:04:11 +01:00
Chaoscaot 25116c3865 Add CreateKitCommand
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
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
Signed-off-by: Chaoscaot <max@maxsp.de>
2025-10-28 23:00:53 +01:00
114 changed files with 1481 additions and 1041 deletions
@@ -100,7 +100,6 @@ public class TickManager21 implements TickManager {
manager.setFrozen(true);
bukkitTask.cancel();
}, 1, 1);
manager.tick();
}
@Override
@@ -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);
}
}
}
@@ -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
@@ -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() {
@@ -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
*/
@@ -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();
}
}
}
-1
View File
@@ -24,5 +24,4 @@ plugins {
dependencies {
testImplementation(libs.junit)
testImplementation(libs.hamcrest)
compileOnly(project(":CommonCore:SQL"))
}
@@ -1,71 +0,0 @@
/*
* 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.logger
import de.steamwar.sql.AuditLog
import de.steamwar.sql.AuditLogTable
import de.steamwar.sql.SQLWrapper
import de.steamwar.sql.SteamwarUser
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.jdbc.insertIgnore
class LogEntry(val type: AuditLog.Type, val user: SteamwarUser) {
private val start = System.currentTimeMillis()
var text: String = ""
var arguments: String = ""
private var exceptionType: String? = null
private var exceptionText: String? = null
private var exceptionStacktrace: String? = null
private var owner: SteamwarUser? = null
fun addException(e: Throwable) {
exceptionType = e.javaClass.name
exceptionText = e.message
exceptionStacktrace = e.stackTraceToString()
}
fun addServerOwner(owner: SteamwarUser) {
this.owner = owner
}
fun finish() {
val end = System.currentTimeMillis()
val info = SQLWrapper.impl.serverInfo
useDb {
AuditLogTable.insertIgnore {
it[this.action] = type
it[this.actor] = user.getId()
it[this.actionText] = text
it[this.actionArguments] = arguments
it[this.duration] = end - start
it[this.errorType] = exceptionType
it[this.errorText] = exceptionText
it[this.stackTrace] = exceptionStacktrace
it[this.server] = info.name
it[this.checkpointed] = info.checkpointed
it[this.duration] = end - start
it[this.serverOwner] = owner?.getId()
}
}
}
}
+68 -21
View File
@@ -19,47 +19,94 @@
package de.steamwar.sql
import de.steamwar.sql.internal.useDb
import org.jetbrains.exposed.v1.core.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IntIdTable
import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass
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 serverType = varchar("ServerType", 255)
val serverOwner = optReference("ServerOwner", SteamwarUserTable)
val checkpointed = bool("Checkpointed")
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")
val actionArguments = text("ActionArguments")
val duration = long("Duration")
val errorType = text("ErrorType").nullable()
val errorText = text("ErrorText").nullable()
val stackTrace = text("StackTrace").nullable()
}
class AuditLog(id: EntityID<Int>): IntEntity(id) {
companion object: IntEntityClass<AuditLog>(AuditLogTable) {
@JvmField
val SERVER_NAME_VELOCITY: String = "Velocity"
const val SERVER_NAME_VELOCITY: String = "Velocity"
private fun create(
serverName: String,
serverOwner: SteamwarUser?,
actor: SteamwarUser,
actionType: Type,
text: String = ""
) = useDb {
new {
this.time = Instant.now()
this.server = serverName
this.serverOwner = serverOwner?.id
this.actor = actor.id
this.action = actionType
this.actionText = text
}
}
@JvmStatic
fun createJoin(jointServerName: String, serverOwner: SteamwarUser?, joinedPlayer: SteamwarUser) = create(jointServerName, serverOwner, joinedPlayer, Type.JOIN)
@JvmStatic
fun createLeave(leftServerName: String, serverOwner: SteamwarUser?, joinedPlayer: SteamwarUser) = create(leftServerName, serverOwner, joinedPlayer, Type.LEAVE)
@JvmStatic
fun createCommand(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser?, command: String) = player?.let { create(serverName, serverOwner, it, Type.COMMAND, command) }
@JvmStatic
fun createSensitiveCommand(
serverName: String,
serverOwner: SteamwarUser?,
player: SteamwarUser?,
command: String
) = player?.let { create(serverName, serverOwner, it, Type.SENSITIVE_COMMAND, command) }
@JvmStatic
fun createChat(serverName: String, serverOwner: SteamwarUser?, chatter: SteamwarUser, chat: String) = create(serverName, serverOwner, chatter, Type.CHAT, chat)
@JvmStatic
fun createGuiOpen(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser, guiName: String) = create(serverName, serverOwner, player, Type.GUI_OPEN, guiName)
@JvmStatic
fun createGuiClick(
serverName: String,
serverOwner: SteamwarUser?,
player: SteamwarUser,
guiName: String,
clickType: String,
slot: Int,
itemName: String
) = create(
serverName,
serverOwner,
player,
Type.GUI_CLICK,
"Gui: $guiName\nSlot: $slot\nClickType: $clickType\nItemName: $itemName"
)
@JvmStatic
fun createGuiClose(serverName: String, serverOwner: SteamwarUser?, player: SteamwarUser, guiName: String) = create(serverName, serverOwner, player, Type.GUI_CLOSE, guiName)
}
var time by AuditLogTable.time
var server by AuditLogTable.server
val serverType by AuditLogTable.serverType
var serverOwner by AuditLogTable.serverOwner
var actor by AuditLogTable.actor
var checkpointed by AuditLogTable.checkpointed
var action by AuditLogTable.action
var actionText by AuditLogTable.actionText
var actionArguments by AuditLogTable.actionArguments
var duration by AuditLogTable.duration
var errorType by AuditLogTable.errorType
var errorText by AuditLogTable.errorText
var stackTrace by AuditLogTable.stackTrace
enum class Type {
JOIN,
@@ -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)
}
@@ -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")
@@ -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)
}
}
+2 -2
View File
@@ -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")
@@ -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")
@@ -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)
@@ -35,7 +35,7 @@ import java.io.InputStream
import java.util.zip.GZIPInputStream
object NodeDataTable: CompositeIdTable("NodeData") {
val nodeId = reference("NodeId", SchematicNodeTable)
val nodeId = reference("NodeId", SchematicNodeTable).index()
val createdAt = timestamp("CreatedAt").defaultExpression(CurrentTimestamp).entityId()
val nodeFormat = enumeration("NodeFormat", NodeData.SchematicFormat::class)
val schemData = blob("SchemData")
@@ -88,8 +88,6 @@ class NodeData(id: EntityID<CompositeID>): CompositeEntity(id) {
schemData.inputStream.let { if(decompress) GZIPInputStream(it) else it }
}
fun schemData() = schemData(true)
override fun delete() = useDb { super.delete() }
enum class SchematicFormat(val fileEnding: String) {
@@ -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.dao.id.EntityID
import org.jetbrains.exposed.v1.core.dao.id.IdTable
import org.jetbrains.exposed.v1.core.eq
@@ -32,8 +33,8 @@ import java.sql.Timestamp
import java.time.Instant
object NodeDownloadTable: IdTable<Int>("NodeDownload") {
override val id = reference("NodeId", SchematicNodeTable).uniqueIndex()
val link = varchar("Link", 255)
override val id = reference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.CASCADE).uniqueIndex()
val link = varchar("Link", 255).uniqueIndex()
val timestamp = timestamp("Timestamp").defaultExpression(CurrentTimestamp)
}
@@ -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.and
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
import org.jetbrains.exposed.v1.core.dao.id.CompositeIdTable
@@ -32,9 +33,9 @@ import java.util.*
import kotlin.jvm.optionals.getOrNull
object NodeMemberTable : CompositeIdTable("NodeMember") {
val node = reference("NodeId", SchematicNodeTable)
val userId = reference("UserId", SteamwarUserTable)
val parentNode = optReference("ParentId", SchematicNodeTable)
val node = reference("NodeId", SchematicNodeTable, onDelete = ReferenceOption.CASCADE, onUpdate = ReferenceOption.CASCADE).index()
val userId = reference("UserId", SteamwarUserTable).index()
val parentNode = optReference("ParentId", SchematicNodeTable).index()
override val primaryKey = PrimaryKey(node, userId)
@@ -94,7 +95,7 @@ class NodeMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
{ Optional.ofNullable(it?.value) })
private set
fun setParentId(id: Int?) {
fun setParentId(id: Int?) = useDb {
parent = Optional.ofNullable(id)
}
@@ -41,6 +41,7 @@ object PersonalKitTable: CompositeIdTable("PersonalKit") {
init {
addIdColumn(userId)
index(false, userId, gamemode)
}
}
@@ -34,12 +34,16 @@ import java.util.function.Consumer
object PunishmentTable : IntIdTable("Punishments", "PunishmentId") {
val userId = reference("UserId", SteamwarUserTable)
val punisher = reference("Punisher", SteamwarUserTable)
val punisher = reference("Punisher", SteamwarUserTable).index()
val type = enumerationByName("Type", 32, Punishment.PunishmentType::class)
val startTime = timestamp("StartTime")
val endTime = timestamp("EndTime")
val perma = bool("Perma")
val reason = text("Reason")
init {
index(false, userId, type)
}
}
class Punishment(id: EntityID<Int>) : IntEntity(id) {
@@ -30,7 +30,7 @@ import org.jetbrains.exposed.v1.dao.CompositeEntityClass
object RefereeTable: CompositeIdTable("Referee") {
val eventId = reference("EventId", EventTable)
val userId = reference("UserId", SteamwarUserTable)
val userId = reference("UserId", SteamwarUserTable).index()
override val primaryKey = PrimaryKey(eventId, userId)
@@ -20,7 +20,6 @@
package de.steamwar.sql;
import de.steamwar.ImplementationProvider;
import de.steamwar.ServerInfo;
import java.io.File;
import java.util.Collections;
@@ -37,10 +36,5 @@ public interface SQLWrapper<M> {
return Collections.emptyList();
}
default void processSchematicType(GameModeConfig<?, String> gameModeConfig) {
}
void additionalExceptionMetadata(StringBuilder builder);
ServerInfo getServerInfo();
}
@@ -36,8 +36,6 @@ object ExceptionTable: IntIdTable("Exception") {
class SWException {
companion object {
private val exceptionCache = HashSet<String>()
val cwd = System.getProperty("user.dir")
val serverName = File(cwd).name
@@ -45,9 +43,7 @@ class SWException {
fun init() = Unit
@JvmStatic
fun log(message: String, stacktrace: String) = if (exceptionCache.contains(stacktrace)) Unit else useDb {
exceptionCache.add(stacktrace)
fun log(message: String, stacktrace: String) = useDb {
ExceptionTable.insert {
it[ExceptionTable.server] = serverName
it[ExceptionTable.message] = generateMessage(message)
@@ -34,13 +34,17 @@ import java.util.*
import java.util.function.Consumer
object SchematicNodeTable : IntIdTable("SchematicNode", "NodeId") {
val owner = reference("NodeOwner", SteamwarUserTable)
val owner = reference("NodeOwner", SteamwarUserTable).index()
val name = varchar("NodeName", 64)
val parent = optReference("ParentNode", SchematicNodeTable)
val parent = optReference("ParentNode", SchematicNodeTable).index()
val lastUpdate = timestamp("LastUpdate").defaultExpression(CurrentTimestamp)
val item = text("NodeItem")
val type = varchar("NodeType", 16).nullable()
val type = varchar("NodeType", 16).nullable().index()
val config = integer("Config")
init {
uniqueIndex(parent, owner, name)
}
}
class SchematicNode(id: EntityID<Int>) : IntEntity(id) {
@@ -146,7 +150,91 @@ class SchematicNode(id: EntityID<Int>) : IntEntity(id) {
@JvmStatic
fun parentsOfNode(user: SteamwarUser, id: Int) = fromSql(
"WITH RECURSIVE R AS (SELECT NodeId, ParentNode FROM EffectiveSchematicNode WHERE NodeId = ? UNION SELECT E.NodeId, E.ParentNode FROM R, EffectiveSchematicNode E WHERE R.ParentNode = E.NodeId AND E.EffectiveOwner = ?) SELECT SN.NodeId, SN.NodeOwner, SN.NodeName, R.ParentNode, SN.LastUpdate, SN.NodeItem, SN.NodeType, SN.NodeRank, SN.Config FROM R INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId",
"""
WITH RECURSIVE
ESN_R AS (
SELECT SchematicNode.NodeId AS NodeId,
NM.UserId AS EffectiveOwner,
NM.ParentId AS ParentNode
FROM SchematicNode
INNER JOIN NodeMember NM ON NM.NodeId = SchematicNode.NodeId
UNION ALL
SELECT S.NodeId AS NodeId,
ESN_R.EffectiveOwner AS EffectiveOwner,
S.ParentNode AS ParentNode
FROM SchematicNode S
INNER JOIN ESN_R ON S.ParentNode = ESN_R.NodeId
),
ESN_R2 AS (
SELECT SchematicNode.NodeId AS NodeId,
NM.UserId AS UserId,
SchematicNode.NodeOwner AS EffectiveOwner,
SchematicNode.ParentNode AS ParentNode
FROM SchematicNode
INNER JOIN NodeMember NM ON NM.NodeId = SchematicNode.NodeId
UNION ALL
SELECT S.NodeId AS NodeId,
ESN_R2.EffectiveOwner AS EffectiveOwner,
ESN_R2.UserId AS UserId,
S.ParentNode AS ParentNode
FROM SchematicNode S
INNER JOIN ESN_R2 ON S.ParentNode = ESN_R2.NodeId
WHERE S.NodeOwner <> ESN_R2.EffectiveOwner
),
ESN_R3 AS (
SELECT SchematicNode.NodeId AS NodeId,
SchematicNode.NodeOwner AS NodeOwner,
SchematicNode.NodeOwner AS EffectiveOwner,
SchematicNode.ParentNode AS ParentNode
FROM SchematicNode
UNION ALL
SELECT S.NodeId AS NodeId,
S.NodeOwner AS NodeOwner,
ESN_R3.EffectiveOwner AS EffectiveOwner,
S.ParentNode AS ParentNode
FROM SchematicNode S
INNER JOIN ESN_R3 ON S.ParentNode = ESN_R3.NodeId
WHERE ESN_R3.NodeOwner <> S.NodeOwner
),
ResolvedNodes AS (
SELECT ESN_R.NodeId AS NodeId,
ESN_R.EffectiveOwner AS EffectiveOwner,
ESN_R.ParentNode AS ParentNode
FROM ESN_R
UNION
SELECT ESN_R2.NodeId AS NodeId,
ESN_R2.UserId AS EffectiveOwner,
ESN_R2.ParentNode AS ParentNode
FROM ESN_R2
INNER JOIN SchematicNode SN2 ON ESN_R2.NodeId = SN2.NodeId
WHERE ESN_R2.ParentNode IS NOT NULL
AND SN2.NodeOwner <> ESN_R2.EffectiveOwner
UNION
SELECT ESN_R3.NodeId AS NodeId,
ESN_R3.EffectiveOwner AS EffectiveOwner,
ESN_R3.ParentNode AS ParentNode
FROM ESN_R3
WHERE ESN_R3.NodeOwner <> ESN_R3.EffectiveOwner
UNION
SELECT SchematicNode.NodeId AS NodeId,
SchematicNode.NodeOwner AS EffectiveOwner,
SchematicNode.ParentNode AS ParentNode
FROM SchematicNode
),
R AS (
SELECT NodeId, ParentNode
FROM ResolvedNodes
WHERE NodeId = ?
UNION
SELECT E.NodeId, E.ParentNode
FROM R
INNER JOIN ResolvedNodes E ON R.ParentNode = E.NodeId
WHERE E.EffectiveOwner = ?
)
SELECT SN.NodeId, SN.NodeOwner, SN.NodeName, R.ParentNode, SN.LastUpdate, SN.NodeItem, SN.NodeType, SN.NodeRank, SN.Config
FROM R
INNER JOIN SchematicNode SN ON SN.NodeId = R.NodeId
""".trimIndent(),
listOf(
IntegerColumnType() to id,
IntegerColumnType() to user.getId()
@@ -284,7 +372,7 @@ class SchematicNode(id: EntityID<Int>) : IntEntity(id) {
val list = mutableListOf<String>()
if (s.contains("/")) {
val preTab = s.take(s.lastIndexOf("/") + 1)
val pa = getNodeFromPath(user, preTab) ?: return emptyList()
val pa = getNodeFromPath(user, preTab) ?: return mutableListOf()
val nodes: List<SchematicNode> = list(user, pa.getId())
val br = pa.generateBreadcrumbs(user)
nodes.forEach(Consumer { node: SchematicNode? -> list.add((if (sws) "/" else "") + br + node!!.name + (if (node.isDir()) "/" else "")) })
@@ -19,11 +19,7 @@
package de.steamwar.sql
import java.io.File
import java.util.*
import java.util.Locale
import java.util.Locale.getDefault
import java.util.stream.Collectors
data class SchematicType(
val name: String,
@@ -47,58 +43,65 @@ data class SchematicType(
@JvmField
val Normal = SchematicType("Normal", "", Type.NORMAL, null, "STONE_BUTTON", false)
private val types: List<SchematicType>
private val fromDB: Map<String, SchematicType>?
private val types: MutableList<SchematicType> = mutableListOf()
private val fromDB: MutableMap<String, SchematicType> = mutableMapOf()
init {
val tmpTypes = mutableListOf<SchematicType>()
val tmpFromDB = mutableMapOf<String, SchematicType>()
tmpTypes.add(Normal)
tmpFromDB[Normal.toDB()] = Normal
val folder = SQLWrapper.impl.schemTypesFolder
if (folder.exists()) {
for (configFile in Arrays.stream<File?>(folder.listFiles { _, name ->
name.endsWith(
".yml"
) && !name.endsWith(".kits.yml")
}).sorted().collect(Collectors.toList())) {
val gameModeConfig = SQLWrapper.impl.loadGameModeConfig(configFile)
if (gameModeConfig.Schematic.Type == null) continue
if (tmpFromDB.containsKey(gameModeConfig.Schematic.Type.toDB())) continue
val current = gameModeConfig.Schematic.Type
if (gameModeConfig.CheckQuestions.isNotEmpty()) {
val checkType = current.checkType
tmpTypes.add(checkType!!)
tmpFromDB[checkType.toDB()] = checkType
}
tmpTypes.add(current)
tmpFromDB[current.toDB()] = current
SQLWrapper.impl.processSchematicType(gameModeConfig)
}
}
types = tmpTypes.toList()
fromDB = tmpFromDB.toMap()
GameModeConfig.init()
init()
}
@JvmStatic
fun values() = types
fun init() {
types.clear()
fromDB.clear()
types.add(Normal)
fromDB[Normal.toDB()] = Normal
for (gameModeConfig in GameModeConfig.getAll<Any>()) {
val type = gameModeConfig.Schematic.Type
?: continue
if (fromDB.containsKey(type.toDB())) continue
types.add(type)
fromDB[type.toDB()] = type
if (gameModeConfig.CheckQuestions.isNotEmpty() && type.checkType != null) {
types.add(type.checkType)
fromDB[type.checkType.toDB()] = type.checkType
}
}
}
@JvmStatic
fun fromDB(value: String) = fromDB?.let { it[value.lowercase()] }
fun values() =
types
@JvmStatic
fun fromDB(value: String) =
fromDB[value.lowercase()]
}
fun name() = name
fun toDB() = name.lowercase()
fun name() =
name
fun check() = type == Type.CHECK_TYPE
fun fightType() = type == Type.FIGHT_TYPE
fun writeable() = type == Type.NORMAL
fun toDB() =
name.lowercase()
fun checkType() = if (manualCheck) checkType else this
fun isAssignable() = type == Type.NORMAL || (type == Type.FIGHT_TYPE && checkType != null) || !manualCheck
fun check() =
type == Type.CHECK_TYPE
fun fightType() =
type == Type.FIGHT_TYPE
fun writeable() =
type == Type.NORMAL
fun checkType() =
if (manualCheck) checkType else this
fun isAssignable() =
type == Type.NORMAL || (type == Type.FIGHT_TYPE && checkType != null) || !manualCheck
enum class Type {
NORMAL,
+5 -1
View File
@@ -28,9 +28,13 @@ import org.jetbrains.exposed.v1.dao.IntEntity
import org.jetbrains.exposed.v1.dao.IntEntityClass
object ScriptTable: IntIdTable("Script") {
val userId = reference("UserId", SteamwarUserTable)
val userId = reference("UserId", SteamwarUserTable).index()
val name = varchar("Name", 64)
val code = text("Code")
init {
uniqueIndex(userId, name)
}
}
class Script(id: EntityID<Int>) : IntEntity(id) {
@@ -28,7 +28,7 @@ import org.jetbrains.exposed.v1.jdbc.insert
import java.sql.Timestamp
object SessionTable: Table("Session") {
val userId = reference("UserId", SteamwarUserTable)
val userId = reference("UserId", SteamwarUserTable).index()
val startTime = timestamp("StartTime")
val endTime = timestamp("EndTime").defaultExpression(CurrentTimestamp)
}
@@ -37,15 +37,15 @@ import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
object SteamwarUserTable : IntIdTable("UserData", "id") {
val uuid = varchar("UUID", 36)
val username = varchar("UserName", 32)
val team = reference("Team", TeamTable)
val uuid = varchar("UUID", 36).uniqueIndex()
val username = varchar("UserName", 32).index()
val team = reference("Team", TeamTable).index()
val leader = bool("Leader")
val locale = varchar("Locale", 16).nullable()
val manualLocale = bool("ManualLocale")
val bedrock = bool("Bedrock")
val password = text("Password").nullable()
val discordId = long("DiscordId").nullable()
val discordId = long("DiscordId").nullable().uniqueIndex()
}
class SteamwarUser(id: EntityID<Int>): IntEntity(id) {
@@ -186,10 +186,10 @@ class SteamwarUser(id: EntityID<Int>): IntEntity(id) {
val salt = ByteArray(16)
random.nextBytes(salt)
val saltString = Base64.getEncoder().encode(salt)
val saltString = Base64.getEncoder().encodeToString(salt)
val hash = generateHash(value, salt)
val hashString = Base64.getEncoder().encode(hash)
val hashString = Base64.getEncoder().encodeToString(hash)
useDb {
passwordInternal = "$hashString:$saltString"
+2 -18
View File
@@ -28,12 +28,10 @@ import org.jetbrains.exposed.v1.dao.IntEntityClass
import org.jetbrains.exposed.v1.jdbc.select
object TeamTable : IntIdTable("Team", "TeamID") {
val kuerzel = varchar("TeamKuerzel", 10)
val kuerzel = varchar("TeamKuerzel", 10).index()
val color = char("TeamColor", 1).default("8")
val name = varchar("TeamName", 16)
val name = varchar("TeamName", 16).index()
val deleted = bool("TeamDeleted").default(false)
val address = text("Address").nullable()
val port = ushort("Port").default(25565u)
}
class Team(id: EntityID<Int>) : IntEntity(id) {
@@ -67,8 +65,6 @@ class Team(id: EntityID<Int>) : IntEntity(id) {
private var name by TeamTable.name
var deleted by TeamTable.deleted
private set
private var teamAddress by TeamTable.address
private var teamPort by TeamTable.port
val members by lazy { useDb { SteamwarUserTable.select(SteamwarUserTable.id).where { SteamwarUserTable.team eq teamId }.map { it[SteamwarUserTable.id].value } } }
fun size() = useDb { SteamwarUser.find { SteamwarUserTable.team eq teamId }.count().toInt() }
@@ -96,16 +92,4 @@ class Team(id: EntityID<Int>) : IntEntity(id) {
set(value) = useDb {
name = value
}
var address: String?
get() = teamAddress
set(value) = useDb {
teamAddress = value
}
var port: Int
get() = teamPort.toInt()
set(value) = useDb {
teamPort = value.toUShort()
}
}
@@ -32,8 +32,8 @@ import org.jetbrains.exposed.v1.jdbc.deleteWhere
import org.jetbrains.exposed.v1.jdbc.insertIgnore
object TeamTeilnahmeTable : CompositeIdTable("TeamTeilnahme") {
val teamId = reference("teamId", TeamTable)
val eventId = reference("eventId", EventTable)
val teamId = reference("teamId", TeamTable).index()
val eventId = reference("eventId", EventTable).index()
val placement = integer("Placement").nullable()
override val primaryKey = PrimaryKey(teamId, eventId)
+3 -3
View File
@@ -33,10 +33,10 @@ import java.sql.Timestamp
import java.util.*
object TokenTable: IntIdTable("Token") {
val name = varchar("Name", 64)
val owner = reference("Owner", SteamwarUserTable)
val name = varchar("Name", 64).uniqueIndex()
val owner = reference("Owner", SteamwarUserTable).index()
val created = timestamp("Created").defaultExpression(CurrentTimestamp)
val hash = varchar("Hash", 88)
val hash = varchar("Hash", 88).uniqueIndex()
}
class Token(id: EntityID<Int>): IntEntity(id) {
@@ -28,7 +28,7 @@ import org.jetbrains.exposed.v1.jdbc.insert
import org.jetbrains.exposed.v1.jdbc.selectAll
object UserPermTable: Table("UserPerm") {
val user = reference("User", SteamwarUserTable.id)
val user = reference("User", SteamwarUserTable.id).index()
val perm = enumerationByName("Perm", 32, UserPerm::class)
override val primaryKey = PrimaryKey(user, perm)
@@ -24,10 +24,7 @@ import org.yaml.snakeyaml.Yaml;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -160,6 +157,22 @@ final class YMLWrapper<M, W> {
}
}
public <K, V> Map<K, V> getMap(String path, Function<String, K> keyFunction, Function<String, V> valueFunction) {
Object data = this.document.get(path);
if (data instanceof Map) {
Map<K, V> result = new HashMap<>();
((Map<String, String>) data).forEach((keyString, valueString) -> {
K key = keyFunction.apply(keyString.toUpperCase());
V value = valueFunction.apply(valueString.toUpperCase());
if (key == null || value == null) return;
result.put(key, value);
});
return Collections.unmodifiableMap(result);
} else {
return Collections.emptyMap();
}
}
public List<Map<?, ?>> getMapList(String path) {
Object value = this.document.get(path);
if (value instanceof List) {
@@ -67,7 +67,7 @@ object KotlinDatabase {
}
}
fun <T: Any?> useDb(statement: JdbcTransaction.() -> T): T {
fun <T> useDb(statement: JdbcTransaction.() -> T): T {
KotlinDatabase.ensureConnected()
return TransactionManager.currentOrNull()?.statement() ?: transaction(KotlinDatabase.db) {
statement()
@@ -42,4 +42,5 @@ dependencies {
compileOnly(libs.fastutil)
compileOnly(libs.authlib)
compileOnly(project(":FightSystem:FightSystem_14"))
}
@@ -0,0 +1,51 @@
/*
* 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.fightsystem.listener;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentTask;
import net.minecraft.world.entity.projectile.windcharge.WindCharge;
import org.bukkit.Location;
public class WindchargeStopper21 implements WindchargeStopper.IWindchargeStopper {
public WindchargeStopper21() {
new StateDependentTask(true, FightState.Running, this::run, 1, 1);
}
private static final int middleLine = Config.SpecSpawn.getBlockZ();
private static final Class<?> windChargeClass = WindCharge.class;
private void run() {
Recording.iterateOverEntities(windChargeClass::isInstance, entity -> {
Location location = entity.getLocation();
Location prevLocation = location.clone().subtract(entity.getVelocity());
boolean passedMiddle = location.getBlockZ() > middleLine && prevLocation.getBlockZ() > middleLine ||
location.getBlockZ() < middleLine && prevLocation.getBlockZ() < middleLine;
if(!passedMiddle) {
entity.remove();
}
});
}
}
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
* 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
@@ -17,11 +17,13 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.logger
package de.steamwar.fightsystem.utils;
import de.steamwar.sql.AuditLog
import de.steamwar.sql.SteamwarUser
import org.bukkit.inventory.ItemStack;
object SWLogger {
fun startCommand(user: SteamwarUser) = LogEntry(AuditLog.Type.COMMAND, user)
}
public class FlatteningWrapper21 extends FlatteningWrapper14 {
@Override
public boolean hasAttributeModifier(ItemStack stack) {
return stack.hasItemMeta() && stack.getItemMeta() != null && stack.getItemMeta().hasAttributeModifiers();
}
}
@@ -38,6 +38,27 @@ public class ReflectionWrapper21 implements ReflectionWrapper {
FORBIDDEN_TYPES.add(DataComponentTypes.BLOCKS_ATTACKS);
FORBIDDEN_TYPES.add(DataComponentTypes.BUNDLE_CONTENTS);
FORBIDDEN_TYPES.add(DataComponentTypes.CUSTOM_MODEL_DATA);
FORBIDDEN_TYPES.add(DataComponentTypes.ATTRIBUTE_MODIFIERS);
FORBIDDEN_TYPES.add(DataComponentTypes.TOOL);
FORBIDDEN_TYPES.add(DataComponentTypes.WEAPON);
FORBIDDEN_TYPES.add(DataComponentTypes.FOOD);
FORBIDDEN_TYPES.add(DataComponentTypes.CONSUMABLE);
FORBIDDEN_TYPES.add(DataComponentTypes.POTION_CONTENTS);
FORBIDDEN_TYPES.add(DataComponentTypes.STORED_ENCHANTMENTS);
FORBIDDEN_TYPES.add(DataComponentTypes.CAN_BREAK);
FORBIDDEN_TYPES.add(DataComponentTypes.CAN_PLACE_ON);
FORBIDDEN_TYPES.add(DataComponentTypes.MAX_DAMAGE);
FORBIDDEN_TYPES.add(DataComponentTypes.USE_REMAINDER);
FORBIDDEN_TYPES.add(DataComponentTypes.USE_COOLDOWN);
FORBIDDEN_TYPES.add(DataComponentTypes.SUSPICIOUS_STEW_EFFECTS);
FORBIDDEN_TYPES.add(DataComponentTypes.CHARGED_PROJECTILES);
FORBIDDEN_TYPES.add(DataComponentTypes.INTANGIBLE_PROJECTILE);
FORBIDDEN_TYPES.add(DataComponentTypes.FIREWORKS);
FORBIDDEN_TYPES.add(DataComponentTypes.FIREWORK_EXPLOSION);
FORBIDDEN_TYPES.add(DataComponentTypes.EQUIPPABLE);
FORBIDDEN_TYPES.add(DataComponentTypes.REPAIR_COST);
FORBIDDEN_TYPES.add(DataComponentTypes.ENCHANTABLE);
}
@Override
@@ -1,7 +1,7 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
* 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
@@ -17,6 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar
package de.steamwar.fightsystem.listener;
data class ServerInfo(val name: String, val version: Int, val checkpointed: Boolean)
public class WindchargeStopper8 implements WindchargeStopper.IWindchargeStopper {
}
@@ -33,9 +33,6 @@ import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.OneShotStateDependent;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.utils.*;
import de.steamwar.fightsystem.winconditions.Wincondition;
import de.steamwar.fightsystem.winconditions.WinconditionComparisonTimeout;
import de.steamwar.fightsystem.winconditions.Winconditions;
import de.steamwar.linkage.AbstractLinker;
import de.steamwar.linkage.SpigotLinker;
import de.steamwar.message.Message;
@@ -43,6 +40,7 @@ import de.steamwar.sql.NodeData;
import de.steamwar.sql.SchematicNode;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.GameRule;
import org.bukkit.plugin.java.JavaPlugin;
public class FightSystem extends JavaPlugin {
@@ -100,6 +98,13 @@ public class FightSystem extends JavaPlugin {
new StateDependentListener(ArenaMode.All, FightState.All, BountifulWrapper.impl.newDenyArrowPickupListener());
new OneShotStateDependent(ArenaMode.All, FightState.PreSchemSetup, () -> Fight.playSound(SWSound.BLOCK_NOTE_PLING.getSound(), 100.0f, 2.0f));
new OneShotStateDependent(ArenaMode.Test, FightState.All, WorldEditRendererCUIEditor::new);
if (Core.getVersion() >= 19) {
try {
Bukkit.getWorlds().get(0).setGameRule(GameRule.REDUCED_DEBUG_INFO, ArenaMode.AntiTest.contains(Config.mode));
} catch (Exception e) {
// Ignore if failed!
}
}
techHider = new TechHiderWrapper();
hullHider = new HullHider();
@@ -45,6 +45,7 @@ import org.bukkit.util.Vector;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.logging.Level;
@@ -179,18 +180,24 @@ public class FightSchematic extends StateDependent {
@Override
public void disable() {
if(!Config.GameModeConfig.Schematic.ReplaceObsidianBedrock || Config.mode == ArenaMode.PREPARE)
if (Config.mode == ArenaMode.PREPARE) {
return;
}
FreezeWorld freezer = null;
if(!Config.GameModeConfig.Schematic.ReplaceWithBlockupdates)
if (!Config.GameModeConfig.Schematic.ReplacementsWithoutBlockUpdates.isEmpty()) {
freezer = new FreezeWorld();
replaceSync(Material.OBSIDIAN, Material.TNT);
replaceSync(Material.BEDROCK, Material.SLIME_BLOCK);
if(!Config.GameModeConfig.Schematic.ReplaceWithBlockupdates)
}
for (Map.Entry<Material, Material> replacement : Config.GameModeConfig.Schematic.ReplacementsWithoutBlockUpdates.entrySet()) {
replaceSync(replacement.getKey(), replacement.getValue());
}
if (freezer != null) {
freezer.disable();
}
for (Map.Entry<Material, Material> replacement : Config.GameModeConfig.Schematic.ReplacementsWithBlockUpdates.entrySet()) {
replaceSync(replacement.getKey(), replacement.getValue());
}
}
public void pasteTeamName(){
@@ -0,0 +1,44 @@
/*
* 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.fightsystem.listener;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.linkage.Linked;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockFormEvent;
@Linked
public class BlockFormListener implements Listener {
public BlockFormListener() {
new StateDependentListener(Config.GameModeConfig.Arena.DisableIceForm, FightState.All, this);
}
@EventHandler
public void onBlockForm(BlockFormEvent event) {
if (Config.ArenaRegion.inRegion(event.getBlock()) && event.getNewState().getType() == Material.ICE) {
event.setCancelled(true);
}
}
}
@@ -181,7 +181,13 @@ public class Permanent implements Listener {
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onExplosion(EntityExplodeEvent e) {
if (!(e.getEntity() instanceof TNTPrimed)) return;
if (!(e.getEntity() instanceof TNTPrimed)) {
if (Config.GameModeConfig.Schematic.Type.toDB().equals("wargearseason26")) {
e.blockList().clear();
}
return;
}
if (!Config.GameModeConfig.Arena.WaterDamage) return;
e.blockList().removeIf(block -> {
if(block.getType() == Material.TNT) {
return false;
@@ -35,10 +35,7 @@ import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.inventory.*;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.ItemStack;
@@ -81,25 +78,8 @@ public class PersonalKitCreator implements Listener {
if(!openKitCreators.containsKey(e.getWhoClicked()))
return;
Player player = (Player) e.getWhoClicked();
//Deny bad items
if(Kit.isBadItem(e.getCursor()))
e.setCancelled(true);
/* Should the inventory reset? */
if(e.getAction() != InventoryAction.PLACE_ALL)
return;
ItemStack[] items = e.getWhoClicked().getInventory().getContents();
for(int i = 0; i < items.length; i++){
ItemStack stack = items[i];
if(stack != null && i != e.getSlot())
return;
}
FightPlayer fightPlayer = Fight.getFightPlayer(player);
assert fightPlayer != null;
Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), () -> fightPlayer.getKit().loadToPlayer(player), 1);
}
@EventHandler
@@ -117,7 +97,10 @@ public class PersonalKitCreator implements Listener {
if(backup == null)
return;
backup.close();
InventoryType type = e.getInventory().getType();
if(type != InventoryType.PLAYER && type != InventoryType.CREATIVE) {
backup.close();
}
}
@EventHandler
@@ -126,7 +109,7 @@ public class PersonalKitCreator implements Listener {
if(backup == null)
return;
backup.close();
Bukkit.getScheduler().runTaskLater(FightSystem.getPlugin(), backup::close, 1);
}
@EventHandler
@@ -151,9 +134,11 @@ public class PersonalKitCreator implements Listener {
}
private void close(){
openKitCreators.remove(player);
Kit kit1 = new Kit(kit.getName(), player);
kit1.removeBadItems();
openKitCreators.remove(player);
kit1.toPersonalKit(kit);
backup.loadToPlayer(player);
player.setGameMode(GameMode.SURVIVAL);
@@ -73,7 +73,7 @@ public class WaterRemover implements Listener {
event.setYield(0); //No drops (additionally to world config)
FightTeam spawn = tnt.remove(event.getEntity().getEntityId());
if(spawn != null && !spawn.getExtendRegion().inRegion(event.getLocation())) {
if(Config.GameModeConfig.Arena.WaterDamage && spawn != null && !spawn.getExtendRegion().inRegion(event.getLocation())) {
Block b = event.getLocation().getBlock();
for(int y = -1; y <= 1; y++) {
for(int z = -1; z <= 1; z++) {
@@ -0,0 +1,35 @@
/*
* 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.fightsystem.listener;
import de.steamwar.core.VersionDependent;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.linkage.Linked;
@Linked
public class WindchargeStopper {
static {
VersionDependent.getVersionImpl(FightSystem.getPlugin());
}
public interface IWindchargeStopper {
}
}
@@ -36,7 +36,6 @@ import de.steamwar.fightsystem.fight.FreezeWorld;
import de.steamwar.fightsystem.listener.FightScoreboard;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.utils.*;
import de.steamwar.sql.SchematicData;
import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.Team;
@@ -520,12 +519,12 @@ public class PacketProcessor implements Listener {
private void pasteEmbeddedSchem(FightTeam team) throws IOException {
int schemId = source.readInt();
Clipboard clipboard = SchematicData.clipboardFromStream(new FilterInputStream(source) {
Clipboard clipboard = WorldEditWrapper.impl.getClipboard(new FilterInputStream(source) {
@Override
public void close() {
// FAWE 1.12 calls close...
}
}, WorldEditWrapper.impl.getNativeFormat());
});
execSync(() -> team.pasteSchem(schemId, clipboard));
}
@@ -283,7 +283,7 @@ public interface Recorder {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
try{
copy(NodeData.getLatest(SchematicNode.getSchematicNode(schemId)).schemData(), buffer);
copy(NodeData.getLatest(SchematicNode.getSchematicNode(schemId)).schemData(true), buffer);
}catch (EOFException e) {
Bukkit.getLogger().log(Level.INFO, "EOFException ignored");
} catch (IOException e) {
@@ -53,7 +53,7 @@ public class AutoChecker15 implements AutoChecker.IAutoChecker {
checkInventory(result, block, material, new BlockPos(x, y, z));
}
if(x == 0 || x == max.getBlockX() - 1 || y == max.getBlockY() - 1 || z == 0 || z == max.getBlockZ() - 1) {
if(x == min.getBlockX() || x == max.getBlockX() || y == max.getBlockY() || z == min.getBlockZ() || z == max.getBlockZ()) {
result.getDesignBlocks().computeIfAbsent(material, m -> new ArrayList<>()).add(new BlockPos(x, y, z));
}
}
@@ -0,0 +1,37 @@
/*
* 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
}
dependencies {
compileOnly(project(":SpigotCore", "default"))
compileOnly(project(":SchematicSystem:SchematicSystem_Core", "default"))
compileOnly(libs.paperapi21) {
attributes {
// Very Hacky, but it works
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 21)
}
}
compileOnly(libs.nms21)
compileOnly(libs.fawe21)
}
@@ -0,0 +1,137 @@
/*
* 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.schematicsystem.autocheck;
import com.sk89q.jnbt.CompoundTag;
import com.sk89q.worldedit.entity.Entity;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BaseBlock;
import de.steamwar.core.Core;
import de.steamwar.sql.GameModeConfig;
import org.bukkit.Material;
import java.util.*;
import java.util.stream.Collectors;
public class AutoChecker21 implements AutoChecker.IAutoChecker {
public AutoChecker.BlockScanResult scan(Clipboard clipboard, GameModeConfig<Material, String> type) {
AutoChecker.BlockScanResult result = new AutoChecker.BlockScanResult();
BlockVector3 min = clipboard.getMinimumPoint();
BlockVector3 max = clipboard.getMaximumPoint();
for (int x = min.getBlockX(); x <= max.getBlockX(); x++) {
for (int y = min.getBlockY(); y <= max.getBlockY(); y++) {
for (int z = min.getBlockZ(); z <= max.getBlockZ(); z++) {
final BaseBlock block = clipboard.getFullBlock(BlockVector3.at(x, y, z));
final Material material = Material.matchMaterial(block.getBlockType().getId());
if (material == null) {
continue;
}
result.getBlockCounts().merge(material, 1, Integer::sum);
if (AutoCheckerItems.impl.getInventoryMaterials().contains(material)) {
checkInventory(result, block, material, new BlockPos(x, y, z), type);
}
if (x == min.getBlockX() || x == max.getBlockX() || y == max.getBlockY() || z == min.getBlockZ() || z == max.getBlockZ()) {
result.getDesignBlocks().computeIfAbsent(material, m -> new ArrayList<>()).add(new BlockPos(x, y, z));
}
}
}
}
return result;
}
private static final Map<Material, Set<Material>> itemsInInv = new EnumMap<>(Material.class);
static {
itemsInInv.put(Material.BUCKET, EnumSet.of(Material.DISPENSER));
itemsInInv.put(Material.TNT, EnumSet.of(Material.CHEST, Material.BARREL, Material.SHULKER_BOX, Material.BLACK_SHULKER_BOX, Material.BLUE_SHULKER_BOX,
Material.BROWN_SHULKER_BOX, Material.CYAN_SHULKER_BOX, Material.GRAY_SHULKER_BOX, Material.GREEN_SHULKER_BOX, Material.LIGHT_BLUE_SHULKER_BOX,
Material.LIGHT_GRAY_SHULKER_BOX, Material.LIME_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.ORANGE_SHULKER_BOX,
Material.PINK_SHULKER_BOX, Material.PURPLE_SHULKER_BOX, Material.RED_SHULKER_BOX, Material.WHITE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX));
itemsInInv.put(Material.FIRE_CHARGE, EnumSet.of(Material.DISPENSER));
itemsInInv.put(Material.ARROW, EnumSet.of(Material.DISPENSER));
AutoCheckerItems.impl.getAllowedMaterialsInInventory().forEach(material -> itemsInInv.put(material, AutoCheckerItems.impl.getInventoryMaterials()));
}
private void checkInventory(AutoChecker.BlockScanResult result, BaseBlock block, Material material, BlockPos pos, GameModeConfig<Material, String> type) {
CompoundTag nbt = block.getNbtData();
if (nbt == null) {
result.getDefunctNbt().add(pos);
return;
}
if (material == Material.JUKEBOX && nbt.getValue().containsKey("RecordItem")) {
result.getRecords().add(pos);
return;
}
List<CompoundTag> items = nbt.getList("Items", CompoundTag.class);
if (items.isEmpty())
return; // Leeres Inventar
int counter = 0;
int windChargeCount = 0;
for (CompoundTag item : items) {
if (!item.containsKey("id")) {
result.getDefunctNbt().add(pos);
continue;
}
Material itemType = Material.matchMaterial(item.getString("id"));
if (itemType == null) // Leere Slots
continue;
if(type.Schematic.Type.getName().equals("wargearseason26") && material == Material.DISPENSER && itemType == Material.WIND_CHARGE) {
windChargeCount += item.getInt("count");
}
else if (!itemsInInv.getOrDefault(itemType, EnumSet.noneOf(Material.class)).contains(material)) {
result.getForbiddenItems().computeIfAbsent(pos, blockVector3 -> new HashSet<>()).add(itemType);
} else if (material == Material.DISPENSER && (itemType == Material.ARROW || itemType == Material.FIRE_CHARGE)) {
counter += Core.getVersion() >= 21 ? item.getInt("count") : item.getByte("Count");
}
if (item.containsKey("tag")) {
result.getForbiddenNbt().computeIfAbsent(pos, blockVector3 -> new HashSet<>()).add(itemType);
}
}
result.getDispenserItems().put(pos, counter);
result.getWindChargeCount().put(pos, windChargeCount);
}
@Override
public AutoCheckerResult check(Clipboard clipboard, GameModeConfig<Material, String> type) {
return AutoCheckerResult.builder().type(type).height(clipboard.getDimensions().getBlockY()).width(clipboard.getDimensions().getBlockX())
.depth(clipboard.getDimensions().getBlockZ()).blockScanResult(scan(clipboard, type))
.entities(clipboard.getEntities().stream().map(Entity::getLocation)
.map(blockVector3 -> new BlockPos(blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ()))
.collect(Collectors.toList()))
.build();
}
@Override
public AutoCheckerResult sizeCheck(Clipboard clipboard, GameModeConfig<Material, String> type) {
return AutoCheckerResult.builder().type(type).height(clipboard.getDimensions().getBlockY()).width(clipboard.getDimensions().getBlockX())
.depth(clipboard.getDimensions().getBlockZ()).build();
}
}
@@ -0,0 +1,49 @@
/*
* 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.schematicsystem.autocheck;
import org.bukkit.Material;
import java.util.EnumSet;
import java.util.Set;
public class AutoCheckerItems21 implements AutoCheckerItems {
private static final Set<Material> INVENTORY = EnumSet.of(Material.BARREL, Material.BLAST_FURNACE, Material.BREWING_STAND, Material.CAMPFIRE,
Material.CHEST, Material.DISPENSER, Material.DROPPER, Material.FURNACE, Material.HOPPER, Material.JUKEBOX, Material.SHULKER_BOX,
Material.WHITE_SHULKER_BOX, Material.ORANGE_SHULKER_BOX, Material.MAGENTA_SHULKER_BOX, Material.LIGHT_BLUE_SHULKER_BOX, Material.YELLOW_SHULKER_BOX,
Material.LIME_SHULKER_BOX, Material.PINK_SHULKER_BOX, Material.GRAY_SHULKER_BOX, Material.LIGHT_GRAY_SHULKER_BOX, Material.CYAN_SHULKER_BOX,
Material.PURPLE_SHULKER_BOX, Material.BLUE_SHULKER_BOX, Material.BROWN_SHULKER_BOX, Material.GREEN_SHULKER_BOX, Material.RED_SHULKER_BOX,
Material.BLACK_SHULKER_BOX, Material.SMOKER, Material.TRAPPED_CHEST);
private static final Set<Material> FLOWERS = EnumSet.of(Material.CORNFLOWER, Material.POPPY, Material.FERN, Material.DANDELION, Material.BLUE_ORCHID,
Material.ALLIUM, Material.AZURE_BLUET, Material.RED_TULIP, Material.ORANGE_TULIP, Material.WHITE_TULIP, Material.PINK_TULIP, Material.OXEYE_DAISY,
Material.LILY_OF_THE_VALLEY, Material.WITHER_ROSE, Material.SUNFLOWER, Material.DIAMOND_HORSE_ARMOR, Material.IRON_HORSE_ARMOR,
Material.GOLDEN_HORSE_ARMOR, Material.LEATHER_HORSE_ARMOR, Material.HONEY_BOTTLE, Material.LILAC, Material.ROSE_BUSH, Material.PEONY,
Material.TALL_GRASS, Material.LARGE_FERN);
@Override
public Set<Material> getInventoryMaterials() {
return INVENTORY;
}
@Override
public Set<Material> getAllowedMaterialsInInventory() {
return FLOWERS;
}
}
@@ -262,6 +262,8 @@ AUTO_CHECKER_RESULT_BLOCKS=§7Blocks: §c{0}§7, Max: §e{1}
AUTO_CHECKER_RESULT_UNKNOWN_MATERIAL=§7Unknown block: §c{0}
AUTO_CHECKER_RESULT_TOO_MANY_BLOCK=§7{0}: §c{1}§7, Max: §e{2}
AUTO_CHECKER_RESULT_FORBIDDEN_BLOCK=§7Forbidden block: §c{0}
AUTO_CHECKER_RESULT_WIND_CHARGES=§7Windcharges: §c{0}§7, Max: §e2048
AUTO_CHECKER_RESULT_WIND_CHARGES_DISPENSER=§7Dispenser: §c[{0}, {1}, {2}]§7, Windcharges: §c{3}§7
AUTO_CHECKER_RESULT_FORBIDDEN_ITEM=§7Forbidden Item: [{0}, {1}, {2}] -> §c{3}
AUTO_CHECKER_RESULT_DEFUNCT_NBT=§7Defunct NBT: §7[{0}, {1}, {2}]
AUTO_CHECKER_RESULT_DESIGN_BLOCK=§7{0} in Design: [{1}, {2}, {3}]
@@ -242,6 +242,8 @@ AUTO_CHECKER_RESULT_BLOCKS=§7Blöcke: §c{0}§7, Max: §e{1}
AUTO_CHECKER_RESULT_UNKNOWN_MATERIAL=§7Unbekannter Block: §c{0}
AUTO_CHECKER_RESULT_TOO_MANY_BLOCK=§7{0}: §c{1}§7, Max: §e{2}
AUTO_CHECKER_RESULT_FORBIDDEN_BLOCK=§7Verbotener Block: §c{0}
AUTO_CHECKER_RESULT_WIND_CHARGES=§7Windcharges: §c{0}§7, Max: §e2048
AUTO_CHECKER_RESULT_WIND_CHARGES_DISPENSER=§7Werfer: §c[{0}, {1}, {2}]§7, Windcharges: §c{3}§7
AUTO_CHECKER_RESULT_FORBIDDEN_ITEM=§7Verbotener gegenstand: [{0}, {1}, {2}] -> §c{3}
AUTO_CHECKER_RESULT_DEFUNCT_NBT=§7Keine NBT-Daten: §c[{0}, {1}, {2}]
AUTO_CHECKER_RESULT_DESIGN_BLOCK=§7{0} im Design: [{1}, {2}, {3}]
@@ -55,6 +55,7 @@ public class AutoChecker {
private final List<BlockPos> records = new ArrayList<>();
private final Map<Material, List<BlockPos>> designBlocks = new EnumMap<>(Material.class);
private final Map<BlockPos, Integer> dispenserItems = new HashMap<>();
private final Map<BlockPos, Integer> windChargeCount = new HashMap<>();
private final Map<BlockPos, Set<Material>> forbiddenItems = new HashMap<>();
private final Map<BlockPos, Set<Material>> forbiddenNbt = new HashMap<>();
}
@@ -20,9 +20,8 @@
package de.steamwar.schematicsystem.autocheck;
import de.steamwar.core.Core;
import de.steamwar.sql.GameModeConfig;
import de.steamwar.schematicsystem.SchematicSystem;
import de.steamwar.sql.SchematicType;
import de.steamwar.sql.GameModeConfig;
import lombok.Builder;
import lombok.Getter;
import lombok.ToString;
@@ -53,6 +52,7 @@ public class AutoCheckerResult {
isBlockCountOk() &&
isLimitedBlocksOK() &&
isDispenserItemsOK() &&
isWindchargeCountOK() &&
!type.isAfterDeadline() &&
entities.isEmpty() &&
isDesignBlastResistanceOK();
@@ -63,8 +63,18 @@ public class AutoCheckerResult {
!type.isAfterDeadline();
}
public boolean isWindchargeCountOK() {
if( type.Schematic.Type.getName().equals("wargearseason26")) {
int windChargesCount = blockScanResult.getWindChargeCount().values().stream().reduce(Integer::sum).orElse(0);
return windChargesCount <= 2048;
}
else {
return true;
}
}
public boolean isDispenserItemsOK() {
return blockScanResult.getDispenserItems().values().stream().allMatch(i -> i <= type.Schematic.MaxDispenserItems);
return blockScanResult.getDispenserItems().values().stream().allMatch(i -> i <= type.Schematic.MaxDispenserItems);
}
public boolean hasWarnings() {
@@ -102,7 +112,9 @@ public class AutoCheckerResult {
}
public boolean isDesignBlastResistanceOK() {
return blockScanResult.getDesignBlocks().keySet().stream().map(Material::getBlastResistance).noneMatch(i -> i > type.Schematic.MaxDesignBlastResistance);
return blockScanResult.getDesignBlocks().keySet().stream()
.filter(material -> material != Material.WATER && material != Material.LAVA)
.map(Material::getBlastResistance).noneMatch(i -> i > type.Schematic.MaxDesignBlastResistance);
}
public void sendErrorMessage(Player p, String schemName) {
@@ -126,6 +138,19 @@ public class AutoCheckerResult {
}
});
}
if(!isWindchargeCountOK()) {
int windChargesCount = blockScanResult.getWindChargeCount().values().stream().reduce(Integer::sum).orElse(0);
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_WIND_CHARGES", p, windChargesCount, 2048);
blockScanResult.getWindChargeCount().entrySet().stream().filter(blockVector3IntegerEntry -> blockVector3IntegerEntry.getValue() > 0).forEach(blockVector3IntegerEntry -> {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_WIND_CHARGES_DISPENSER", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3IntegerEntry.getKey()),
blockVector3IntegerEntry.getKey().getBlockX(),
blockVector3IntegerEntry.getKey().getBlockY(),
blockVector3IntegerEntry.getKey().getBlockZ(),
blockVector3IntegerEntry.getValue());
});
}
blockScanResult.getDispenserItems().entrySet().stream().filter(blockVector3IntegerEntry -> blockVector3IntegerEntry.getValue() > type.Schematic.MaxDispenserItems).forEach(blockVector3IntegerEntry -> {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_TOO_MANY_DISPENSER_ITEMS", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3IntegerEntry.getKey()),
blockVector3IntegerEntry.getKey().getBlockX(),
@@ -134,6 +159,7 @@ public class AutoCheckerResult {
blockVector3IntegerEntry.getValue(),
type.Schematic.MaxDispenserItems);
});
blockScanResult.getRecords().forEach(blockVector3 -> {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_RECORD", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(blockVector3), blockVector3.getBlockX(), blockVector3.getBlockY(), blockVector3.getBlockZ());
});
@@ -148,6 +174,7 @@ public class AutoCheckerResult {
});
if(Core.getVersion() > 12) {
blockScanResult.getDesignBlocks().forEach((material, poss) -> {
if (material == Material.WATER || material == Material.LAVA) return;
if(material.getBlastResistance() > type.Schematic.MaxDesignBlastResistance) {
poss.forEach(pos -> {
SchematicSystem.MESSAGE.sendPrefixless("AUTO_CHECKER_RESULT_DESIGN_BLOCK", p, SchematicSystem.MESSAGE.parse("AUTO_CHECKER_RESULT_TELEPORT_HERE", p), tpCommandTo(pos), material.name(), pos.getBlockX(), pos.getBlockY(), pos.getBlockZ());
+1
View File
@@ -32,4 +32,5 @@ dependencies {
implementation(project(":SchematicSystem:SchematicSystem_15"))
implementation(project(":SchematicSystem:SchematicSystem_19"))
implementation(project(":SchematicSystem:SchematicSystem_20"))
implementation(project(":SchematicSystem:SchematicSystem_21"))
}
@@ -67,36 +67,31 @@ public class WorldEditWrapper14 implements WorldEditWrapper {
}
@Override
public void setPlayerClipboard(Player player, InputStream is, NodeData.SchematicFormat schemFormat) {
Clipboard clipboard = null;
try {
clipboard = getClipboard(is, schemFormat);
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
if (clipboard == null)
throw new NoClipboardException();
public void setPlayerClipboard(Player player, Clipboard clipboard) {
Actor actor = WorldEditWrapper.getWorldEditPlugin().wrapCommandSender(player);
WorldEditWrapper.getWorldEditPlugin().getWorldEdit().getSessionManager().get(actor).setClipboard(new ClipboardHolder(clipboard));
}
@Override
public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException {
try {
public Clipboard getClipboard(NodeData data) throws IOException {
InputStream is = data.schemData(true);
return readClipboard(is, data.getNodeFormat());
}
switch (schemFormat) {
case SPONGE_V2:
case SPONGE_V3:
return new SpongeSchematicReader(new NBTInputStream(is), this).read();
case MCEDIT:
return new MCEditSchematicReader(new NBTInputStream(is)).read();
default:
throw new IOException("This schematic format is currently not supported");
}
} catch (NullPointerException e) {
throw new NoClipboardException();
@Override
public Clipboard getClipboard(InputStream inputStream) throws IOException {
return readClipboard(inputStream, getNativeFormat());
}
private Clipboard readClipboard(InputStream is, NodeData.SchematicFormat format) throws IOException {
switch (format) {
case SPONGE_V2:
case SPONGE_V3:
return new SpongeSchematicReader(new NBTInputStream(is), this).read();
case MCEDIT:
return new MCEditSchematicReader(new NBTInputStream(is)).read();
default:
throw new IOException("This schematic format is currently not supported");
}
}
@@ -93,7 +93,7 @@ public class ChunkHider18 implements ChunkHider {
private static final Reflection.Method getKey = Reflection.getTypedMethod(IRegistry.class, null, MinecraftKey.class, Object.class);
private static final Reflection.Method getName = Reflection.getTypedMethod(MinecraftKey.class, null, String.class);
protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) {
return !hiddenBlockEntities.contains((String) getName.invoke(getKey.invoke(tileEntityTypes, entityType.get(tile))));
return !hiddenBlockEntities.contains(TechHider.normalizeBlockEntityId((String) getName.invoke(getKey.invoke(tileEntityTypes, entityType.get(tile)))));
}
private void blocks(SectionHider section) {
@@ -20,8 +20,11 @@
package de.steamwar.techhider;
import de.steamwar.Reflection;
import de.steamwar.core.Core;
import net.minecraft.core.IRegistry;
import net.minecraft.core.SectionPosition;
import net.minecraft.network.protocol.game.PacketPlayOutBlockBreak;
import net.minecraft.resources.MinecraftKey;
import net.minecraft.world.level.block.entity.TileEntitySign;
import net.minecraft.world.level.block.entity.TileEntityTypes;
import net.minecraft.world.level.block.state.IBlockData;
@@ -88,6 +91,14 @@ public class ProtocolWrapper18 implements ProtocolWrapper {
return tileEntityType.get(packet) != signType;
}
private static final IRegistry<?> tileEntityTypes = Reflection.getField(Core.getVersion() > 18 ? Reflection.getClass("net.minecraft.core.registries.BuiltInRegistries") : IRegistry.class, IRegistry.class, 0, TileEntityTypes.class).get(null);
private static final Reflection.Method getKey = Reflection.getTypedMethod(IRegistry.class, null, MinecraftKey.class, Object.class);
private static final Reflection.Method getName = Reflection.getTypedMethod(MinecraftKey.class, null, String.class);
@Override
public String tileEntityDataType(Object packet) {
return TechHider.normalizeBlockEntityId((String) getName.invoke(getKey.invoke(tileEntityTypes, tileEntityType.get(packet))));
}
@Override
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) {
return (p, packet) -> {
@@ -19,6 +19,7 @@
package de.steamwar.techhider;
import de.steamwar.Reflection;
import org.bukkit.entity.Player;
import java.util.function.BiFunction;
@@ -27,6 +28,7 @@ public class ProtocolWrapper19 extends ProtocolWrapper18 {
@Override
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) {
return null;
Reflection.Field<?> blockBreakPosition = Reflection.getField(blockBreakPacket, TechHider.blockPosition, 0);
return (p, packet) -> techHider.getLocationEvaluator().checkBlockPos(p, blockBreakPosition.get(packet)) == TechHider.State.SKIP ? packet : null;
}
}
@@ -19,12 +19,13 @@
package de.steamwar.core;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV2;
import com.fastasyncworldedit.core.extent.clipboard.io.FastSchematicReaderV3;
import com.sk89q.jnbt.NBTInputStream;
import com.sk89q.worldedit.extension.platform.Actor;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.extent.clipboard.io.BuiltInClipboardFormat;
import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter;
import com.sk89q.worldedit.extent.clipboard.io.MCEditSchematicReader;
import com.sk89q.worldedit.extent.clipboard.io.*;
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV1Reader;
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV2Reader;
import com.sk89q.worldedit.extent.clipboard.io.sponge.SpongeSchematicV3Reader;
import com.sk89q.worldedit.math.Vector3;
@@ -37,10 +38,12 @@ import org.bukkit.util.Vector;
import org.enginehub.linbus.stream.LinBinaryIO;
import java.io.DataInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
public class WorldEditWrapper21 implements WorldEditWrapper {
@@ -54,43 +57,68 @@ public class WorldEditWrapper21 implements WorldEditWrapper {
}
@Override
public void setPlayerClipboard(Player player, InputStream is, NodeData.SchematicFormat schemFormat) {
Clipboard clipboard = null;
try {
clipboard = getClipboard(is, schemFormat);
} catch (IOException e) {
throw new RuntimeException(e.getMessage(), e);
}
if (clipboard == null)
throw new SecurityException("Clipboard is null");
public void setPlayerClipboard(Player player, Clipboard clipboard) {
Actor actor = WorldEditWrapper.getWorldEditPlugin().wrapCommandSender(player);
WorldEditWrapper.getWorldEditPlugin().getWorldEdit().getSessionManager().get(actor).setClipboard(new ClipboardHolder(clipboard));
}
@Override
@SuppressWarnings("removal")
public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat ignored) throws IOException {
ResetableInputStream ris = new ResetableInputStream(is);
for (NodeData.SchematicFormat schemFormat : NodeData.SchematicFormat.values()) {
try {
Clipboard clipboard = switch (schemFormat) {
case MCEDIT -> new MCEditSchematicReader(new NBTInputStream(ris)).read();
case SPONGE_V2 -> new SpongeSchematicV2Reader(LinBinaryIO.read(new DataInputStream(ris))).read();
case SPONGE_V3 -> new SpongeSchematicV3Reader(LinBinaryIO.read(new DataInputStream(ris))).read();
};
ris.close();
return clipboard;
} catch (Exception e) {
// Ignore
}
ris.reset();
public Clipboard getClipboard(NodeData data) throws IOException {
ResetableInputStream is = new ResetableInputStream(data.schemData(false));
for (ClipboardFormat clipboardFormat : ClipboardFormats.getAll()) {
FilterInputStream fis = new FilterInputStream(is) {
@Override
public void close() throws IOException {
// Ignore close call!
}
};
boolean canBeRead = clipboardFormat.isFormat(fis);
is.reset();
if (!canBeRead) continue;
return clipboardFormat.load(is);
}
throw new IOException("No clipboard found");
}
private class ResetableInputStream extends InputStream {
private static final Function<InputStream, ClipboardReader> FastV3 = FastSchematicReaderV3::new;
@SuppressWarnings("removal")
private static final Function<InputStream, ClipboardReader> FastV2 = inputStream -> new FastSchematicReaderV2(new NBTInputStream(inputStream));
@SuppressWarnings("removal")
private static final Function<InputStream, ClipboardReader> McEdit = inputStream -> new MCEditSchematicReader(new NBTInputStream(inputStream));
private static final Function<InputStream, ClipboardReader> SpongeV3 = inputStream -> new SpongeSchematicV3Reader(LinBinaryIO.read(new DataInputStream(inputStream)));
private static final Function<InputStream, ClipboardReader> SpongeV2 = inputStream -> new SpongeSchematicV2Reader(LinBinaryIO.read(new DataInputStream(inputStream)));
private static final Function<InputStream, ClipboardReader> SpongeV1 = inputStream -> new SpongeSchematicV1Reader(LinBinaryIO.read(new DataInputStream(inputStream)));
private static final Function<InputStream, ClipboardReader>[] READERS = new Function[]{
FastV3,
FastV2,
SpongeV3,
SpongeV2,
SpongeV1,
McEdit
};
@Override
public Clipboard getClipboard(InputStream inputStream) throws IOException {
ResetableInputStream is = new ResetableInputStream(inputStream);
for (Function<InputStream, ClipboardReader> reader : READERS) {
FilterInputStream fis = new FilterInputStream(is) {
@Override
public void close() throws IOException {
// Ignore close call!
}
};
try {
return reader.apply(fis).read();
} catch (Exception e) {
is.reset();
}
}
is.close();
throw new IOException("No clipboard found");
}
private static class ResetableInputStream extends InputStream {
private InputStream inputStream;
private int pointer = 0;
@@ -25,16 +25,20 @@ import io.netty.buffer.Unpooled;
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
import net.minecraft.util.SimpleBitStorage;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.Set;
import java.util.function.IntUnaryOperator;
import java.util.function.BiFunction;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
public class ChunkHider21 implements ChunkHider {
private static final int DIRECT_BLOCK_BITS = Math.max(9, Integer.SIZE - Integer.numberOfLeadingZeros(Block.BLOCK_STATE_REGISTRY.size() - 1));
@Override
public Class<?> mapChunkPacket() {
return ClientboundLevelChunkWithLightPacket.class;
@@ -96,33 +100,70 @@ public class ChunkHider21 implements ChunkHider {
private static final Reflection.Method getKey = Reflection.getTypedMethod(registry, "getKey", resourceLocation, Object.class);
private static final Reflection.Method getName = Reflection.getTypedMethod(resourceLocation, "getPath", String.class);
protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) {
return !hiddenBlockEntities.contains(getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile))));
return !hiddenBlockEntities.contains(TechHider.normalizeBlockEntityId((String) getName.invoke(getKey.invoke(nameField.get(null), entityType.get(tile)))));
}
private void blocks(SectionHider section) {
section.copyBitsPerBlock();
int sourceBitsPerBlock = section.readBitsPerBlock();
boolean singleValued = section.getBitsPerBlock() == 0;
if(section.isSkipSection()) {
section.writeBitsPerBlock(sourceBitsPerBlock);
copyBlockStorage(section);
return;
}
boolean singleValued = sourceBitsPerBlock == 0;
if (singleValued) {
int value = ProtocolUtils.readVarInt(section.getIn());
ProtocolUtils.writeVarInt(section.getOut(), !section.isSkipSection() && section.getObfuscate().contains(value) ? section.getTarget() : value);
if(section.blockPrecise()) {
writeDirectBlocks(section, DIRECT_BLOCK_BITS, ignored -> value);
} else {
section.writeBitsPerBlock(sourceBitsPerBlock);
ProtocolUtils.writeVarInt(section.getOut(), section.getObfuscate().contains(value) ? section.getTarget() : value);
}
return;
} else if (section.getBitsPerBlock() < 9) {
} else if (sourceBitsPerBlock < 9) {
if(section.blockPrecise()) {
int[] palette = section.readPalette();
SimpleBitStorage values = new SimpleBitStorage(sourceBitsPerBlock, 4096, section.readNewDataArray(4096));
writeDirectBlocks(section, DIRECT_BLOCK_BITS, pos -> palette[values.get(pos)]);
return;
}
section.writeBitsPerBlock(sourceBitsPerBlock);
// Indirect (paletted) storage – only present when bitsPerBlock < 9 in 1.21+
section.processPalette();
}
if (section.isSkipSection() || (!section.blockPrecise() && section.isPaletted())) {
if (!section.blockPrecise() && section.isPaletted()) {
section.skipNewDataArray(4096);
return;
}
SimpleBitStorage values = new SimpleBitStorage(section.getBitsPerBlock(), 4096, section.readNewDataArray(4096));
SimpleBitStorage values = new SimpleBitStorage(sourceBitsPerBlock, 4096, section.readNewDataArray(4096));
writeDirectBlocks(section, sourceBitsPerBlock, values::get);
}
private void copyBlockStorage(SectionHider section) {
if(section.getBitsPerBlock() == 0) {
section.copyVarInt();
} else {
if(section.getBitsPerBlock() < 9)
section.skipPalette();
section.skipNewDataArray(4096);
}
}
private void writeDirectBlocks(SectionHider section, int bitsPerBlock, IntUnaryOperator source) {
section.writeBitsPerBlock(bitsPerBlock);
SimpleBitStorage values = new SimpleBitStorage(bitsPerBlock, 4096);
for (int y = 0; y < 16; y++) {
for (int z = 0; z < 16; z++) {
for (int x = 0; x < 16; x++) {
int pos = (((y * 16) + z) * 16) + x;
int value = source.applyAsInt(pos);
TechHider.State test = section.test(x, y, z);
@@ -130,15 +171,17 @@ public class ChunkHider21 implements ChunkHider {
case SKIP:
break;
case CHECK:
if (!section.getObfuscate().contains(values.get(pos)))
if (!section.getObfuscate().contains(value))
break;
case HIDE:
values.set(pos, section.getTarget());
value = section.getTarget();
break;
case HIDE_AIR:
default:
values.set(pos, section.getAir());
value = section.getAir();
}
values.set(pos, value);
}
}
}
@@ -0,0 +1,104 @@
/*
* 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.techhider;
import de.steamwar.Reflection;
import net.minecraft.core.BlockPos;
import net.minecraft.core.SectionPos;
import net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.function.BiFunction;
public class ProtocolWrapper21 implements ProtocolWrapper {
private static final Reflection.Field<SectionPos> multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPos.class, 0);
private static final Reflection.Field<short[]> multiBlockChangePos = Reflection.getField(TechHider.multiBlockChangePacket, short[].class, 0);
private static final Reflection.Field<BlockState[]> multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, BlockState[].class, 0);
@Override
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider) {
return (p, packet) -> {
TechHider.LocationEvaluator locationEvaluator = techHider.getLocationEvaluator();
SectionPos sectionPos = multiBlockChangeChunk.get(packet);
int chunkX = sectionPos.x();
int chunkY = sectionPos.y();
int chunkZ = sectionPos.z();
if(locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ))
return packet;
packet = TechHider.multiBlockChangeCloner.apply(packet);
final short[] oldPos = multiBlockChangePos.get(packet);
final BlockState[] oldBlocks = multiBlockChangeBlocks.get(packet);
ArrayList<Short> poss = new ArrayList<>(oldPos.length);
ArrayList<BlockState> blocks = new ArrayList<>(oldPos.length);
for(int i = 0; i < oldPos.length; i++) {
short pos = oldPos[i];
BlockState block = oldBlocks[i];
switch(locationEvaluator.check(p, 16*chunkX + SectionPos.sectionRelativeX(pos), 16*chunkY + SectionPos.sectionRelativeY(pos), 16*chunkZ + SectionPos.sectionRelativeZ(pos))) {
case SKIP:
poss.add(pos);
blocks.add(block);
break;
case CHECK:
poss.add(pos);
blocks.add(techHider.iBlockDataHidden(block) ? (BlockState) techHider.getObfuscationTarget() : block);
break;
default:
break;
}
}
if(blocks.isEmpty())
return null;
short[] newPos = new short[poss.size()];
for(int i = 0; i < newPos.length; i++)
newPos[i] = poss.get(i);
multiBlockChangePos.set(packet, newPos);
multiBlockChangeBlocks.set(packet, blocks.toArray(new BlockState[0]));
return packet;
};
}
private static final Reflection.Field<BlockEntityType> tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, BlockEntityType.class, 0);
@Override
public boolean unfilteredTileEntityDataAction(Object packet) {
BlockEntityType type = tileEntityType.get(packet);
return type != BlockEntityType.SIGN && type != BlockEntityType.HANGING_SIGN;
}
@Override
public String tileEntityDataType(Object packet) {
return BlockEntityType.getKey(tileEntityType.get(packet)).getPath();
}
private static final Reflection.Field<BlockPos> blockBreakPosition = Reflection.getField(ClientboundBlockDestructionPacket.class, BlockPos.class, 0);
@Override
public BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider) {
return (p, packet) -> techHider.getLocationEvaluator().checkBlockPos(p, blockBreakPosition.get(packet)) == TechHider.State.SKIP ? packet : null;
}
}
@@ -22,7 +22,10 @@ package de.steamwar.core;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.sk89q.jnbt.*;
import com.sk89q.worldedit.*;
import com.sk89q.worldedit.BlockVector;
import com.sk89q.worldedit.Vector;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseBlock;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.extension.input.ParserContext;
@@ -57,22 +60,25 @@ public class WorldEditWrapper8 implements WorldEditWrapper {
}
@Override
public void setPlayerClipboard(Player player, InputStream is, NodeData.SchematicFormat schemFormat) {
public void setPlayerClipboard(Player player, Clipboard clipboard) {
WorldData world = new BukkitWorld(player.getWorld()).getWorldData();
Clipboard clipboard;
try {
clipboard = getClipboard(is, schemFormat);
} catch (IOException e) {
throw new RuntimeException(e);
}
Actor actor = WorldEditWrapper.getWorldEditPlugin().wrapCommandSender(player);
WorldEditWrapper.getWorldEditPlugin().getWorldEdit().getSessionManager().get(actor).setClipboard(new ClipboardHolder(clipboard, world));
}
@Override
public Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException {
switch (schemFormat) {
public Clipboard getClipboard(NodeData data) throws IOException {
InputStream is = data.schemData(true);
return readClipboard(is, data.getNodeFormat());
}
@Override
public Clipboard getClipboard(InputStream inputStream) throws IOException {
return readClipboard(inputStream, getNativeFormat());
}
private Clipboard readClipboard(InputStream is, NodeData.SchematicFormat format) throws IOException {
switch (format) {
case MCEDIT:
return new SchematicReader(new NBTInputStream(is)).read(WorldEdit.getInstance().getServer().getWorlds().get(0).getWorldData());
case SPONGE_V2:
@@ -54,7 +54,7 @@ public class ChunkHider9 extends ChunkHider8 {
packet = mapChunkCloner.apply(packet);
Set<String> hiddenBlockEntities = techHider.getHiddenBlockEntities();
mapChunkBlockEntities.set(packet, ((List<?>)mapChunkBlockEntities.get(packet)).stream().filter(
nbttag -> !hiddenBlockEntities.contains((String) nbtTagGetString.invoke(nbttag, "id"))
nbttag -> !hiddenBlockEntities.contains(TechHider.normalizeBlockEntityId((String) nbtTagGetString.invoke(nbttag, "id")))
).collect(Collectors.toList()));
int primaryBitMask = mapChunkBitMask.get(packet);
@@ -23,6 +23,7 @@ import de.steamwar.Reflection;
import de.steamwar.Reflection.Field;
import de.steamwar.core.Core;
import io.netty.channel.*;
import io.netty.util.ReferenceCountUtil;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@@ -37,6 +38,7 @@ import org.bukkit.plugin.Plugin;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import java.util.logging.Level;
@@ -57,17 +59,29 @@ public class TinyProtocol implements Listener {
public static final Field<List> networkManagers = Reflection.getField(serverConnection, List.class, 0, networkManager);
private static final String HANDLER_NAME = "tiny-steamwar";
private static final Class<?> bundlePacket = getOptionalClass("net.minecraft.network.protocol.BundlePacket");
private static final Class<?> clientboundBundlePacket = getOptionalClass("net.minecraft.network.protocol.game.ClientboundBundlePacket");
private static final Reflection.Method bundleSubPackets = bundlePacket == null ? null : Reflection.getTypedMethod(bundlePacket, null, Iterable.class);
private static final Reflection.Constructor clientboundBundleConstructor = clientboundBundlePacket == null ? null : Reflection.getConstructor(clientboundBundlePacket, Iterable.class);
public static final TinyProtocol instance = new TinyProtocol(Core.getInstance());
public static void init() {
//enforce init
}
private static Class<?> getOptionalClass(String className) {
try {
return Reflection.getClass(className);
} catch (IllegalArgumentException ignored) {
return null;
}
}
private final Plugin plugin;
private final List<?> connections;
private boolean closed;
private final Map<Class<?>, List<BiFunction<Player, Object, Object>>> packetFilters = new HashMap<>();
private final Map<Class<?>, List<BiFunction<Player, Object, Object>>> packetFilters = new ConcurrentHashMap<>();
@Getter
private final Map<Player, PacketInterceptor> playerInterceptors = new HashMap<>();
@@ -207,31 +221,51 @@ public class TinyProtocol implements Listener {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Object originalMsg = msg;
try {
msg = filterPacket(player, msg);
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error during incoming packet processing", e);
msg = null;
}
if (msg != null) {
super.channelRead(ctx, msg);
} else {
ReferenceCountUtil.release(originalMsg);
}
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
Object originalMsg = msg;
try {
msg = filterPacket(player, msg);
} catch (Exception e) {
plugin.getLogger().log(Level.SEVERE, "Error during outgoing packet processing", e);
msg = null;
}
if (msg != null) {
super.write(ctx, msg, promise);
} else {
ReferenceCountUtil.release(originalMsg);
promise.trySuccess();
}
}
private Object filterPacket(Player player, Object packet) {
packet = filterSinglePacket(player, packet);
if(packet == null)
return null;
if(clientboundBundlePacket != null && clientboundBundlePacket.isInstance(packet))
return filterBundlePacket(player, packet);
return packet;
}
private Object filterSinglePacket(Player player, Object packet) {
List<BiFunction<Player, Object, Object>> filters = packetFilters.getOrDefault(packet.getClass(), Collections.emptyList());
for(BiFunction<Player, Object, Object> filter : filters) {
@@ -243,5 +277,33 @@ public class TinyProtocol implements Listener {
return packet;
}
private Object filterBundlePacket(Player player, Object packet) {
if(bundleSubPackets == null || clientboundBundleConstructor == null)
throw new IllegalStateException("Cannot filter bundled packet " + packet.getClass().getName());
ArrayList<Object> filteredPackets = new ArrayList<>();
boolean changed = false;
for(Object subPacket : (Iterable<?>) bundleSubPackets.invoke(packet)) {
Object filteredPacket = filterPacket(player, subPacket);
if(filteredPacket == null) {
changed = true;
continue;
}
if(filteredPacket != subPacket)
changed = true;
filteredPackets.add(filteredPacket);
}
if(!changed)
return packet;
if(filteredPackets.isEmpty())
return null;
return clientboundBundleConstructor.invoke(filteredPackets);
}
}
}
@@ -19,14 +19,9 @@
package de.steamwar.core;
import lombok.Getter;
public class CheckpointUtils {
private CheckpointUtils() {}
@Getter
private static boolean restored = false;
public static void signalHandler() {
try {
CheckpointUtilsJ9.signalHandler();
@@ -38,7 +33,6 @@ public class CheckpointUtils {
public static void freeze() {
try {
CheckpointUtilsJ9.freeze();
restored = true;
} catch (NoClassDefFoundError e) {
//ignore
}
@@ -22,10 +22,10 @@ package de.steamwar.core;
import com.sk89q.worldedit.EmptyClipboardException;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.extent.clipboard.Clipboard;
import com.sk89q.worldedit.session.ClipboardHolder;
import de.steamwar.sql.NoClipboardException;
import com.sk89q.worldedit.math.transform.Transform;
import com.sk89q.worldedit.regions.Region;
import com.sk89q.worldedit.session.ClipboardHolder;
import de.steamwar.sql.NoClipboardException;
import de.steamwar.sql.NodeData;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
@@ -38,8 +38,10 @@ public interface WorldEditWrapper {
WorldEditWrapper impl = VersionDependent.getVersionImpl(Core.getInstance());
InputStream getPlayerClipboard(Player player);
void setPlayerClipboard(Player player, InputStream is, NodeData.SchematicFormat schemFormat);
Clipboard getClipboard(InputStream is, NodeData.SchematicFormat schemFormat) throws IOException;
void setPlayerClipboard(Player player, Clipboard clipboard);
Clipboard getClipboard(NodeData data) throws IOException;
Clipboard getClipboard(InputStream inputStream) throws IOException;
Vector getOrigin(Clipboard clipboard);
Vector getMinimum(Region region);
@@ -19,8 +19,6 @@
package de.steamwar.sql;
import de.steamwar.ServerInfo;
import de.steamwar.core.CheckpointUtils;
import de.steamwar.core.Core;
import de.steamwar.data.GameModeConfigUtils;
import org.bukkit.Bukkit;
@@ -70,9 +68,4 @@ public class SQLWrapperImpl implements SQLWrapper<Material> {
builder.append(world.getName()).append(" ");
builder.append("\nServer: ").append(SERVER_VERSION);
}
@Override
public ServerInfo getServerInfo() {
return new ServerInfo(Core.getServerName(), Core.getVersion(), CheckpointUtils.isRestored());
}
}
@@ -29,14 +29,6 @@ import java.io.InputStream;
public class SchematicData {
public static Clipboard clipboardFromStream(InputStream is, NodeData.SchematicFormat schemFormat) {
try {
return WorldEditWrapper.impl.getClipboard(is, schemFormat);
} catch (IOException e) {
throw new SecurityException("Could not read schem", e);
}
}
private final NodeData data;
public SchematicData(SchematicNode node) {
@@ -60,11 +52,11 @@ public class SchematicData {
}
public Clipboard load() throws IOException, NoClipboardException {
return WorldEditWrapper.impl.getClipboard(data.schemData(), data.getNodeFormat());
return WorldEditWrapper.impl.getClipboard(data);
}
public void loadToPlayer(Player player) throws IOException, NoClipboardException {
WorldEditWrapper.impl.setPlayerClipboard(player, data.schemData(), data.getNodeFormat());
WorldEditWrapper.impl.setPlayerClipboard(player, WorldEditWrapper.impl.getClipboard(data));
}
public static void saveFromPlayer(Player player, SchematicNode node) throws IOException, NoClipboardException {
@@ -92,7 +92,17 @@ public interface ChunkHider {
}
public void copyBitsPerBlock() {
bitsPerBlock = in.readByte();
bitsPerBlock = in.readUnsignedByte();
out.writeByte(bitsPerBlock);
}
public int readBitsPerBlock() {
bitsPerBlock = in.readUnsignedByte();
return bitsPerBlock;
}
public void writeBitsPerBlock(int bitsPerBlock) {
this.bitsPerBlock = bitsPerBlock;
out.writeByte(bitsPerBlock);
}
@@ -108,6 +118,15 @@ public interface ChunkHider {
copyVarInt();
}
public int[] readPalette() {
int paletteLength = ProtocolUtils.readVarInt(in);
int[] palette = new int[paletteLength];
for(int i = 0; i < paletteLength; i++)
palette[i] = ProtocolUtils.readVarInt(in);
return palette;
}
public void processPalette() {
if(skipSection) {
skipPalette();
@@ -98,10 +98,7 @@ public class ProtocolUtils {
}
public static int posToChunk(int c){
int chunk = c / 16;
if(c < 0)
chunk--;
return chunk;
return Math.floorDiv(c, 16);
}
@Deprecated
@@ -31,6 +31,10 @@ public interface ProtocolWrapper {
boolean unfilteredTileEntityDataAction(Object packet);
default String tileEntityDataType(Object packet) {
return null;
}
BiFunction<Player, Object, Object> blockBreakHiderGenerator(Class<?> blockBreakPacket, TechHider techHider);
BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider);
@@ -27,6 +27,7 @@ import org.bukkit.Material;
import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
@@ -67,11 +68,12 @@ public class TechHider {
private final Set<Integer> obfuscateIds;
@Getter
private final Set<String> hiddenBlockEntities;
private boolean enabled;
public TechHider(LocationEvaluator locationEvaluator, Material obfuscationTarget, Set<Material> obfuscate, Set<String> hiddenBlockEntities) {
this.locationEvaluator = locationEvaluator;
this.obfuscateIds = obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet());
this.hiddenBlockEntities = hiddenBlockEntities;
this.hiddenBlockEntities = hiddenBlockEntities.stream().map(TechHider::normalizeBlockEntityId).collect(Collectors.toSet());
this.obfuscationTarget = getBlockDataByBlock.invoke(getBlockByMaterial.invoke(null, obfuscationTarget));
this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget);
@@ -81,9 +83,11 @@ public class TechHider {
techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this));
techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(this));
if(Core.getVersion() > 12 && Core.getVersion() < 19) {
if(Core.getVersion() > 12 && Core.getVersion() < 19 || Core.getVersion() >= 21) {
Class<?> blockBreakClass = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundBlockDestructionPacket");
techhiders.put(blockBreakClass, ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, this));
BiFunction<Player, Object, Object> blockBreakHider = ProtocolWrapper.impl.blockBreakHiderGenerator(blockBreakClass, this);
if(blockBreakHider != null)
techhiders.put(blockBreakClass, blockBreakHider);
}
if(Core.getVersion() > 8){
@@ -94,10 +98,18 @@ public class TechHider {
}
public void enable() {
if(enabled)
return;
enabled = true;
techhiders.forEach(TinyProtocol.instance::addFilter);
}
public void disable() {
if(!enabled)
return;
enabled = false;
techhiders.forEach(TinyProtocol.instance::removeFilter);
}
@@ -142,6 +154,8 @@ public class TechHider {
case SKIP:
return packet;
case CHECK:
if(isHiddenBlockEntity(ProtocolWrapper.impl.tileEntityDataType(packet)))
return null;
if(ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet))
return packet;
default:
@@ -149,6 +163,19 @@ public class TechHider {
}
}
public boolean isHiddenBlockEntity(String id) {
return hiddenBlockEntities.contains(normalizeBlockEntityId(id));
}
public static String normalizeBlockEntityId(String id) {
if(id == null)
return "";
id = id.toLowerCase(Locale.ROOT);
int namespaceSeparator = id.indexOf(':');
return namespaceSeparator < 0 ? id : id.substring(namespaceSeparator + 1);
}
public enum State {
SKIP,
CHECK,
@@ -1,181 +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.teamserver.listener;
import com.sk89q.worldedit.bukkit.BukkitWorld;
import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.regions.selector.CuboidRegionSelector;
import com.sk89q.worldedit.world.World;
import de.steamwar.core.WorldEditRenderer;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MinVersion;
import de.steamwar.teamserver.Builder;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
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.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector;
import java.util.*;
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),
};
private Map<Player, Selector> selectors = new HashMap<>();
@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;
Selector selector = selectors.get(event.getPlayer());
if (selector != null) selector.cancel();
Material material = event.getPlayer().getInventory().getItemInOffHand().getType();
if (material.isAir()) {
selector = new Selector(event.getClickedBlock(), event.getPlayer(), __ -> true);
} else {
selector = new Selector(event.getClickedBlock(), event.getPlayer(), type -> type == material);
}
selectors.put(event.getPlayer(), selector);
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
Selector selector = selectors.remove(event.getPlayer());
if (selector != null) selector.cancel();
}
private static final WorldEditPlugin WORLDEDIT_PLUGIN = Objects.requireNonNull((WorldEditPlugin) Bukkit.getPluginManager().getPlugin("WorldEdit"));
private static final World BUKKITWORLD = new BukkitWorld(Bukkit.getWorlds().get(0));
private class Selector {
private static final int MAX_BLOCKS = 1_000_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<>();
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();
bukkitTask = Bukkit.getScheduler().runTaskTimer(Builder.getInstance(), () -> {
run();
long volume = (long)(maxX - minX + 1) * (long)(maxY - minY + 1) * (long)(maxZ - minZ + 1);
player.sendTitle("", "§e" + volume + " §7Blocks", 0, 5, 0);
WORLDEDIT_PLUGIN.getSession(player).setRegionSelector(BUKKITWORLD, new CuboidRegionSelector(BUKKITWORLD, BlockVector3.at(minX, minY, minZ), BlockVector3.at(maxX, maxY, maxZ)));
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);
}
}, 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);
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);
}
}
}
}
}
@@ -22,6 +22,7 @@ package de.steamwar.towerrun.commands;
import de.steamwar.command.SWCommand;
import de.steamwar.command.TypeValidator;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.sql.UserPerm;
import de.steamwar.towerrun.TowerRun;
@@ -30,6 +31,8 @@ import org.bukkit.entity.Player;
@Linked
public class StartCommand extends SWCommand {
@LinkedInstance
private LobbyCountdown countdown;
public StartCommand() {
@@ -159,8 +159,8 @@ public class TowerGenerator {
noKeyFloors--;
if (!chestBlocks.isEmpty() && noKeyFloors < 0 && random.nextDouble() < config.keyChance) {
noKeyFloors = random.nextInt(config.maxNoKeyFloors - config.minNoKeyFloors) + config.minNoKeyFloors;
for (int i = 0; i < 2; i++) {
Container container = chestBlocks.get(random.nextInt(chestBlocks.size()));
for (int i = 0; i < 2 && !chestBlocks.isEmpty(); i++) {
Container container = chestBlocks.remove(random.nextInt(chestBlocks.size()));
keys.add(container.getLocation());
}
@@ -42,6 +42,7 @@ import org.bukkit.event.entity.EntityRegainHealthEvent;
import org.bukkit.event.entity.ItemSpawnEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
@@ -158,10 +159,16 @@ public class IngameListener extends GameStateBukkitListener {
public void onKeyUse(PlayerInteractEvent event) {
if (!event.hasItem()) return;
if (event.getItem().getType() != Material.LEVER) return;
event.setCancelled(true);
if (!event.hasBlock()) return;
if (event.getClickedBlock().getType() != Material.IRON_DOOR) return;
event.getPlayer().getInventory().setItemInMainHand(null);
if (event.getHand() == null) return;
event.setCancelled(true);
ItemStack itemStack = event.getItem();
itemStack.setAmount(event.getItem().getAmount() - 1);
switch (event.getHand()) {
case OFF_HAND -> event.getPlayer().getInventory().setItemInOffHand(itemStack);
case HAND -> event.getPlayer().getInventory().setItemInMainHand(itemStack);
}
event.getClickedBlock().breakNaturally();
}
@@ -223,6 +230,8 @@ public class IngameListener extends GameStateBukkitListener {
shouldMelt(block.getRelative(0, 0, -1));
}
private static final Random RANDOM = new Random();
private void shouldMelt(Block block) {
if (block.getType().isBurnable()) return;
if (block.getType().isAir()) return;
@@ -269,7 +278,9 @@ public class IngameListener extends GameStateBukkitListener {
break;
}
Pos pos = new Pos(block.getLocation().getBlockX(), block.getLocation().getBlockY(), block.getLocation().getBlockZ());
blocksToMelt.putIfAbsent(pos, time + meltingTime + 1);
int delay = meltingTime + 1 + RANDOM.nextInt(30*20)-30*10;
if (delay < 0) delay = meltingTime + 1;
blocksToMelt.putIfAbsent(pos, time + delay);
}
@EventHandler
@@ -42,6 +42,10 @@ public abstract class OutsideWincondition extends WinCondition {
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (event.getTo().getY() > WorldConfig.ESCAPE_HEIGHT) {
if (event.getTo().getY() > WorldConfig.ESCAPE_HEIGHT + 10 && Arrays.stream(WorldConfig.REGIONS).noneMatch(region -> region.contains(event.getTo().toVector()))) {
TowerRunPlayer tPlayer = TowerRunPlayer.get(event.getPlayer());
tPlayer.player().damage(Integer.MAX_VALUE);
}
return;
}
@@ -57,7 +57,6 @@ dependencies {
implementation(libs.mysql)
implementation(libs.msgpack)
implementation(libs.apolloprotos)
implementation(libs.nbt)
@@ -419,7 +419,6 @@ TEAM_NOT_IN_EVENT=§cThis is not possible during an event.
TEAM_HELP_HEADER=§7Manage your team with §e/team.
TEAM_HELP_LIST=§8/§7team list §8- §7List all teams.
TEAM_HELP_INFO=§8/§7team info §8- §7Get information on a team.
TEAM_HELP_TP=§8/§7team tp §8(§7Team§8) §8- §7Teleport to a team server.
TEAM_HELP_CREATE=§8/§7team create §8- §7Create your own team.
TEAM_HELP_JOIN=§8/§7team join §8- §7Join a team.
TEAM_HELP_CHAT=§8/§7teamchat §8- §7Send messages to your team.
@@ -391,7 +391,6 @@ TEAM_NOT_IN_EVENT=§cDies ist während eines Events nicht möglich.
TEAM_HELP_HEADER=§7Mit §e/team §7verwaltest du dein Team.
TEAM_HELP_LIST=§8/§7team list §8- §7Liste alle Teams auf.
TEAM_HELP_INFO=§8/§7team info §8- §7Informiere dich über ein Team.
TEAM_HELP_TP=§8/§7team tp §8(§7Team§8) §8- §7Teleportiert zum Teamserver.
TEAM_HELP_CREATE=§8/§7team create §8- §7Erstelle dein eigenes Team.
TEAM_HELP_JOIN=§8/§7team join §8- §7Trete einem Team bei.
TEAM_HELP_CHAT=§8/§7teamchat §8- §7Sende Nachrichten an dein Team.

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