65 Commits

Author SHA1 Message Date
26a45fabb1 Merge pull request '1.21.6 Fightsystem' (#134) from 1.21.6/fightsystem into main
All checks were successful
SteamWarCI Build successful
Reviewed-on: #134
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
2025-08-08 23:48:37 +02:00
f8bb69e829 Merge branch 'main' into 1.21.6/fightsystem
All checks were successful
SteamWarCI Build successful
2025-08-05 21:13:24 +02:00
b74b73b871 BauSystem/BauSystem_Main/src/de/steamwar/bausystem/features/techhider/TechHiderCommand.java aktualisiert
All checks were successful
SteamWarCI Build successful
2025-08-05 21:12:35 +02:00
464d7c85bd Update version handling for Minecraft 1.21.5: Adjust TypeUtils and ServerStarter to support PAPER_21.
All checks were successful
SteamWarCI Build successful
2025-08-03 22:10:15 +02:00
285882be70 Add chunk reset logic for Minecraft 1.21 in CraftbukkitWrapper21 to support advanced chunk management.
All checks were successful
SteamWarCI Build successful
2025-08-03 16:42:05 +02:00
50780ad9bd Add chunk reset logic for Minecraft 1.21 in CraftbukkitWrapper21 to support advanced chunk management.
All checks were successful
SteamWarCI Build successful
2025-08-03 16:39:25 +02:00
45646b6ba3 Update SelectAdjacent
All checks were successful
SteamWarCI Build successful
2025-08-02 13:23:03 +02:00
8d1b15b019 Fix PistonCalculator
All checks were successful
SteamWarCI Build successful
2025-08-02 13:14:05 +02:00
2e91a5a582 Trigger rebuild
All checks were successful
SteamWarCI Build successful
2025-08-02 11:42:39 +02:00
018b9a971f Fix stuff in DiscordBot
All checks were successful
SteamWarCI Build successful
2025-08-02 11:36:08 +02:00
70d0f179cc Fix stuff in DiscordBot
All checks were successful
SteamWarCI Build successful
2025-08-02 11:34:41 +02:00
2166096ba5 Fix PistonCalculator
All checks were successful
SteamWarCI Build successful
2025-08-01 21:17:58 +02:00
3a13fc7bb9 Reduce MAX_BLOCKS of SelectAdjacent
All checks were successful
SteamWarCI Build successful
2025-08-01 17:51:19 +02:00
01f55c4309 Fix VacationCommand
All checks were successful
SteamWarCI Build successful
2025-08-01 10:51:17 +02:00
d968187750 Fix VacationCommand
All checks were successful
SteamWarCI Build successful
2025-08-01 10:49:28 +02:00
9e629d09a8 Fix VacationCommand
All checks were successful
SteamWarCI Build successful
2025-08-01 10:48:17 +02:00
d14022e69e Fix ChannelListener
All checks were successful
SteamWarCI Build successful
2025-08-01 10:46:54 +02:00
6623e9d808 Add VacationCommand
All checks were successful
SteamWarCI Build successful
2025-08-01 10:43:37 +02:00
9279d9cd8e Improve SelectAdjacent
All checks were successful
SteamWarCI Build successful
2025-07-31 18:37:02 +02:00
b7963f2fe6 Fix PistonCalculator
All checks were successful
SteamWarCI Build successful
2025-07-31 18:00:38 +02:00
3f88ea1e57 Fix SelectAdjacent but make it a touch slower
All checks were successful
SteamWarCI Build successful
2025-07-31 16:30:02 +02:00
8076f31a19 Fix SelectAdjacent but make it a touch slower
All checks were successful
SteamWarCI Build successful
2025-07-31 16:25:05 +02:00
33e9b3409f Add SelectAdjacent for BauSystem and Builder
All checks were successful
SteamWarCI Build successful
2025-07-31 16:21:08 +02:00
e7803dcf82 Enhance compatibility and feature support for Minecraft 1.21: Add ProtocolWrapper21, update gamerule management, streamline entity tracking, and refine chunk hider logic.
All checks were successful
SteamWarCI Build successful
2025-07-31 11:34:56 +02:00
cf52b50333 Introduce support for Minecraft 1.21: Add ReflectionWrapper21, ChunkHider21, and enhance version compatibility across systems.
All checks were successful
SteamWarCI Build successful
2025-07-31 10:43:40 +02:00
297aa6151d Pot fix RPlayer
All checks were successful
SteamWarCI Build successful
2025-07-31 09:44:28 +02:00
14be3a8e3b Fix CheckpointUtilsJ9
All checks were successful
SteamWarCI Build successful
2025-07-30 21:01:18 +02:00
1c1e2e2efe Fix CheckpointUtilsJ9
All checks were successful
SteamWarCI Build successful
2025-07-30 20:59:03 +02:00
e30eb35792 Fix CheckpointUtilsJ9
All checks were successful
SteamWarCI Build successful
2025-07-30 20:57:38 +02:00
e94f273e21 Fix CheckpointUtilsJ9
All checks were successful
SteamWarCI Build successful
2025-07-30 20:56:52 +02:00
c76648e630 Fix CheckpointUtilsJ9
All checks were successful
SteamWarCI Build successful
2025-07-30 20:54:18 +02:00
a943dd6014 Fix CheckpointUtilsJ9
All checks were successful
SteamWarCI Build successful
2025-07-30 20:51:17 +02:00
0d7776ec6f Fix CheckpointUtilsJ9
All checks were successful
SteamWarCI Build successful
2025-07-30 20:47:27 +02:00
7db1d80988 Add SpaceCraft to startable fight servers
All checks were successful
SteamWarCI Build successful
2025-07-30 20:41:01 +02:00
50bd18bae7 Fix DevCommand
All checks were successful
SteamWarCI Build successful
2025-07-30 18:20:41 +02:00
89a55996fc Fix steamwar.devserver.gradle
All checks were successful
SteamWarCI Build successful
2025-07-30 16:45:08 +02:00
f494729b89 Update steamwar.devserver.gradle
All checks were successful
SteamWarCI Build successful
2025-07-30 16:03:39 +02:00
bcabce8b23 Fix JVMCRIUException
All checks were successful
SteamWarCI Build successful
2025-07-30 14:00:47 +02:00
376da2028b Fix ColorCommand
Some checks failed
SteamWarCI Build failed
2025-07-30 13:36:24 +02:00
b6cfaf8135 Fix BauSystem.properties NO_PERMISSION
Some checks failed
SteamWarCI Build failed
2025-07-30 13:34:51 +02:00
5daee0f589 Fix checkpointing of a dev server
Some checks failed
SteamWarCI Build failed
2025-07-30 11:36:40 +02:00
43663ea099 Fix CRIUSupport for local building
Some checks failed
SteamWarCI Build failed
2025-07-30 11:27:09 +02:00
9d1b0063b9 Hotfix RayVisualizerCommand
All checks were successful
SteamWarCI Build successful
2025-07-29 17:55:38 +02:00
e706044f44 Add RayVisualizerCommand
All checks were successful
SteamWarCI Build successful
Closes: #99
2025-07-29 17:46:12 +02:00
332daec716 Enable Loader.setTicksBetweenShots before loader finished
All checks were successful
SteamWarCI Build successful
Closes: #76
2025-07-29 15:37:56 +02:00
2daca017c2 Add InventoryFillerCommand.gui
All checks were successful
SteamWarCI Build successful
Closes: #27
Fix PistonCalculator movement
2025-07-29 14:52:03 +02:00
7edd72ac27 Improve PistonCalculator last time
All checks were successful
SteamWarCI Build successful
2025-07-29 12:32:25 +02:00
1355b57a93 Improve Subserver.run for CRIU
All checks were successful
SteamWarCI Build successful
2025-07-29 12:04:48 +02:00
e04f0d4ad1 Revert the PR merge
All checks were successful
SteamWarCI Build successful
2025-07-29 12:03:33 +02:00
79a6c93f8f Merge pull request 'Improve Subserver.run for checkpoint' (#116) from VelocityCore/ImproveCheckpointHandling into main
All checks were successful
SteamWarCI Build successful
Reviewed-on: #116
2025-07-29 11:56:39 +02:00
e36bbfd2a7 Improve PistonCalculator
All checks were successful
SteamWarCI Build successful
2025-07-29 11:18:04 +02:00
65cb544b64 Merge pull request 'Implement anvil inventory handling through SWAnvilInv and related network packets' (#117) from VelocityCore/anvil-inv into main
All checks were successful
SteamWarCI Build successful
Reviewed-on: #117
Reviewed-by: YoyoNow <yoyonow@noreply.localhost>
2025-07-29 11:06:18 +02:00
8a22afab63 Handle NullPointerException in handleClick method of SWAnvilInv to prevent crashes
All checks were successful
SteamWarCI Build successful
2025-07-29 11:05:46 +02:00
caae9542f4 Fix CLOSE case in SWAnvilInv to properly remove player from openInv map
All checks were successful
SteamWarCI Build successful
2025-07-29 11:03:00 +02:00
4104c60f6b Hotfix PistonCalculator
All checks were successful
SteamWarCI Build successful
2025-07-29 10:52:18 +02:00
69260a9b73 Hotfix PistonCalculator
All checks were successful
SteamWarCI Build successful
2025-07-29 10:15:08 +02:00
4fb6505aef Hotfix PistonCalculator
All checks were successful
SteamWarCI Build successful
2025-07-29 10:09:55 +02:00
bb2f7cf0c1 Hotfix PistonCalculator
All checks were successful
SteamWarCI Build successful
2025-07-29 10:09:14 +02:00
8516c8e536 Closes: #120
All checks were successful
SteamWarCI Build successful
Fix Loader.single
2025-07-29 09:59:38 +02:00
066f06a6e3 Closes: #121
All checks were successful
SteamWarCI Build successful
Improve PistonCalculator
2025-07-29 09:53:17 +02:00
78e176b55e Closes: #94
All checks were successful
SteamWarCI Build successful
2025-07-28 22:08:39 +02:00
86a10b3e97 Remove unused imports and redundant method annotations in SavePart class
All checks were successful
SteamWarCI Build successful
2025-07-26 20:59:45 +02:00
d01efffb6a Add steamshrimp.de to known hostnames in Hostname class
All checks were successful
SteamWarCI Build successful
2025-07-20 00:45:56 +02:00
eac0390dd3 Implement anvil inventory handling through SWAnvilInv and related network packets
All checks were successful
SteamWarCI Build successful
2025-07-19 22:34:36 +02:00
933e2119ef Improve Subserver.run for checkpoint
All checks were successful
SteamWarCI Build successful
2025-07-19 18:20:48 +02:00
68 changed files with 2341 additions and 163 deletions

View File

@@ -25,7 +25,7 @@ PAGE_LIST=§e Page ({0}/{1}) »»
LIST_PREVIOUS_PAGE=§ePrevious page
LIST_NEXT_PAGE=§eNext page
# Permissions
NO_PERMISSION=You are not allowed to use that here
NO_PERMISSION=§7You are not allowed to use that here
SPECTATOR=§fSpectator
# Scoreboard
SCOREBOARD_TIME=Time
@@ -390,6 +390,12 @@ INVENTORY_FILL_HELP=§8/§einventoryfill §8- §7Toggles InventoryFill
INVENTORY_FILL_INFO=§7Helps you fill containers by looking at them while sneaking and dropping the item. Or just scroll on a container to change the amount of the item inside.
INVENTORY_FILL_ENABLE=§aInventoryFiller activated
INVENTORY_FILL_DISABLE=§cInventoryFiller deactivated
INVENTORY_FILL_GUI_NAME=Inventory Filler
INVENTORY_FILL_GUI_POWER=§ePower§8:§7 {0}
INVENTORY_FILL_GUI_TNT=§eTNT
# Ray Visualizer
RAY_VISUALIZER_ENABLE=§aRayVisualizer activated
RAY_VISUALIZER_DISABLE=§aRayVisualizer deactivated
# Killchecker
KILLCHECKER_HELP_ENABLE=§8/§ekillchecker enable §8- §7Enables Killchecker / Recalculates kills
KILLCHECKER_HELP_DISABLE=§8/§ekillchecker disable §8- §7Disables Killchecker

View File

@@ -25,7 +25,7 @@ PAGE_LIST=§e Seite ({0}/{1}) »»
LIST_PREVIOUS_PAGE=§eVorherige Seite
LIST_NEXT_PAGE=§eNächste Seite
# Permission
NO_PERMISSION=Du darfst dies hier nicht nutzen
NO_PERMISSION=§7Du darfst dies hier nicht nutzen
SPECTATOR=§fZuschauer
# Scoreboard
SCOREBOARD_TIME=Uhrzeit
@@ -350,8 +350,11 @@ SMART_PLACE_DISABLE=§cSmartPlace deaktiviert
# InventoryFiller
INVENTORY_FILL_HELP=§8/§einventoryfill §8- §7Toggled InventoryFill
INVENTORY_FILL_INFO=§7Hilft dir, Behälter zu füllen, indem du sie beim sneaken ansiehst und den Gegenstand fallen lässt. Oder scrolle einfach auf einen Behälter, um die Menge des gehaltenen Gegenstandes darin zu ändern.
INVENTORY_FILL_ENABLE=§aInventoryFiller activated
INVENTORY_FILL_DISABLE=§cInventoryFiller deactivated
INVENTORY_FILL_ENABLE=§aInventoryFiller aktiviert
INVENTORY_FILL_DISABLE=§cInventoryFiller deaktiviert
# Ray Visualizer
RAY_VISUALIZER_ENABLE=§aRayVisualizer aktiviert
RAY_VISUALIZER_DISABLE=§aRayVisualizer deaktiviert
# Killchecker
KILLCHECKER_HELP_ENABLE=§8/§ekillchecker enable §8- §7Aktiviert Killchecker / Berechnet kills neu
KILLCHECKER_HELP_DISABLE=§8/§ekillchecker disable §8- §7Deaktiviert Killchecker

View File

@@ -4,12 +4,45 @@ import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.SWUtils;
import de.steamwar.bausystem.configplayer.Config;
import de.steamwar.command.SWCommand;
import de.steamwar.inventory.SWInventory;
import de.steamwar.inventory.SWItem;
import de.steamwar.linkage.Linked;
import org.bukkit.Material;
import org.bukkit.block.Barrel;
import org.bukkit.block.BlockState;
import org.bukkit.block.Chest;
import org.bukkit.block.ShulkerBox;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.BlockStateMeta;
import java.util.HashMap;
import java.util.Map;
@Linked
public class InventoryFillerCommand extends SWCommand {
private static final Map<Integer, Integer> POWER_TO_FILLLEVEL = new HashMap<>();
static {
POWER_TO_FILLLEVEL.put(0, 0);
POWER_TO_FILLLEVEL.put(1, 1);
POWER_TO_FILLLEVEL.put(2, 1 * 64 + 60);
POWER_TO_FILLLEVEL.put(3, 3 * 64 + 55);
POWER_TO_FILLLEVEL.put(4, 5 * 64 + 51);
POWER_TO_FILLLEVEL.put(5, 7 * 64 + 46);
POWER_TO_FILLLEVEL.put(6, 9 * 64 + 42);
POWER_TO_FILLLEVEL.put(7, 11 * 64 + 37);
POWER_TO_FILLLEVEL.put(8, 13 * 64 + 32);
POWER_TO_FILLLEVEL.put(9, 15 * 64 + 28);
POWER_TO_FILLLEVEL.put(10, 17 * 64 + 23);
POWER_TO_FILLLEVEL.put(11, 19 * 64 + 19);
POWER_TO_FILLLEVEL.put(12, 21 * 64 + 14);
POWER_TO_FILLLEVEL.put(13, 23 * 64 + 10);
POWER_TO_FILLLEVEL.put(14, 25 * 64 + 5);
POWER_TO_FILLLEVEL.put(15, 27 * 64 + 0);
}
public InventoryFillerCommand() {
super("inventoryfill");
}
@@ -21,8 +54,116 @@ public class InventoryFillerCommand extends SWCommand {
if (!inventoryFill) {
SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("INVENTORY_FILL_ENABLE", player));
BauSystem.MESSAGE.send("INVENTORY_FILL_INFO", player);
}else {
} else {
SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("INVENTORY_FILL_DISABLE", player));
}
}
@Register("gui")
public void gui(Player player) {
SWInventory inventory = new SWInventory(player, 18, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_NAME", player));
inventory.setItem(0, new SWItem(Material.REDSTONE, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 0)).setAmount(1));
inventory.setItem(1, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 1)).setAmount(1));
inventory.setItem(2, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 2)).setAmount(2));
inventory.setItem(3, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 3)).setAmount(3));
inventory.setItem(4, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 4)).setAmount(4));
inventory.setItem(5, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 5)).setAmount(5));
inventory.setItem(6, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 6)).setAmount(6));
inventory.setItem(7, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 7)).setAmount(7));
inventory.setItem(8, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 8)).setAmount(8));
inventory.setItem(9, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 9)).setAmount(9));
inventory.setItem(10, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 10)).setAmount(10));
inventory.setItem(11, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 11)).setAmount(11));
inventory.setItem(12, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 12)).setAmount(12));
inventory.setItem(13, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 13)).setAmount(13));
inventory.setItem(14, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 14)).setAmount(14));
inventory.setItem(15, new SWItem(Material.REDSTONE_TORCH, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_POWER", player, 15)).setAmount(15));
inventory.setItem(17, new SWItem(Material.TNT, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_TNT", player)));
for (int i = 0; i < 16; i++) {
inventory.setEventCallback(i, inventoryClickEvent -> {
selectInventoryType(player, inventoryClickEvent.getCurrentItem().getItemMeta().getDisplayName(), Material.DANDELION, POWER_TO_FILLLEVEL.get(inventoryClickEvent.getCurrentItem().getAmount()));
});
}
inventory.setEventCallback(17, inventoryClickEvent -> {
selectInventoryType(player, inventoryClickEvent.getCurrentItem().getItemMeta().getDisplayName(), Material.TNT, POWER_TO_FILLLEVEL.get(15));
});
inventory.open();
}
private final Material[] SHULKER_BOX_MATERIALS = new Material[]{
Material.SHULKER_BOX,
Material.WHITE_SHULKER_BOX,
Material.LIGHT_GRAY_SHULKER_BOX,
Material.GRAY_SHULKER_BOX,
Material.BLACK_SHULKER_BOX,
Material.BROWN_SHULKER_BOX,
Material.RED_SHULKER_BOX,
Material.ORANGE_SHULKER_BOX,
Material.YELLOW_SHULKER_BOX,
Material.LIME_SHULKER_BOX,
Material.GREEN_SHULKER_BOX,
Material.CYAN_SHULKER_BOX,
Material.LIGHT_BLUE_SHULKER_BOX,
Material.BLUE_SHULKER_BOX,
Material.PURPLE_SHULKER_BOX,
Material.MAGENTA_SHULKER_BOX,
Material.PINK_SHULKER_BOX
};
private void selectInventoryType(Player player, String name, Material material, int count) {
ItemStack[] itemStacks = new ItemStack[27];
int index = 0;
int amount = count;
while (amount > 0) {
ItemStack stack;
if (amount > 64) {
stack = new ItemStack(material, 64);
amount -= 64;
} else {
stack = new ItemStack(material, amount);
amount = 0;
}
itemStacks[index++] = stack;
}
SWInventory inventory = new SWInventory(player, 27, BauSystem.MESSAGE.parse("INVENTORY_FILL_GUI_NAME", player));
int i = 0;
for (Material type : SHULKER_BOX_MATERIALS) {
inventory.setItemEvent(i++, generateFilledInventory(name, type, itemStacks), inventoryClickEvent -> {
SWUtils.giveItemToPlayer(player, inventoryClickEvent.getCurrentItem());
});
}
inventory.setItemEvent(27 - 6, generateFilledInventory(name, Material.CHEST, itemStacks), inventoryClickEvent -> {
SWUtils.giveItemToPlayer(player, inventoryClickEvent.getCurrentItem());
});
inventory.setItemEvent(27 - 4, generateFilledInventory(name, Material.BARREL, itemStacks), inventoryClickEvent -> {
SWUtils.giveItemToPlayer(player, inventoryClickEvent.getCurrentItem());
});
inventory.open();
}
private ItemStack generateFilledInventory(String name, Material material, ItemStack[] itemStacks) {
ItemStack itemStack = new ItemStack(material);
BlockStateMeta meta = (BlockStateMeta) itemStack.getItemMeta();
BlockState state = meta.getBlockState();
if (state instanceof ShulkerBox box) {
box.getInventory().setContents(itemStacks);
box.update();
meta.setBlockState(box);
} else if (state instanceof Chest chest) {
chest.getInventory().setContents(itemStacks);
chest.update();
meta.setBlockState(chest);
} else if (state instanceof Barrel barrel) {
barrel.getInventory().setContents(itemStacks);
barrel.update();
meta.setBlockState(barrel);
}
meta.setDisplayName(name);
itemStack.setItemMeta(meta);
return itemStack;
}
}

View File

@@ -73,7 +73,7 @@ public class Loader implements Listener {
Bukkit.getPluginManager().registerEvents(this, BauSystem.getInstance());
BauSystem.runTaskTimer(BauSystem.getInstance(), () -> {
if (stage != Stage.RUNNING) return;
if (stage != Stage.RUNNING && stage != Stage.SINGLE) return;
if(!Permission.BUILD.hasPermission(p)) return;
if (waitTime > 0) {
waitTime--;
@@ -149,13 +149,16 @@ public class Loader implements Listener {
}
public boolean setTicksBetweenShots(int delay) {
if (elements.size() == 0) return false;
if (elements.isEmpty()) return false;
LoaderElement loaderElement = elements.get(elements.size() - 1);
if (loaderElement instanceof LoaderWait) {
((LoaderWait) loaderElement).setDelay(delay);
return true;
} else {
LoaderWait loaderWait = new LoaderWait(delay);
elements.add(loaderWait);
pause();
}
return false;
return true;
}
public void setTicksBetweenBlocks(int delay) {

View File

@@ -0,0 +1,198 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.rayvisualizer;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.bausystem.SWUtils;
import de.steamwar.bausystem.configplayer.Config;
import de.steamwar.bausystem.utils.BauMemberUpdateEvent;
import de.steamwar.command.SWCommand;
import de.steamwar.entity.CRay;
import de.steamwar.entity.REntityServer;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MinVersion;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.bukkit.entity.TNTPrimed;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.util.RayTraceResult;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Linked
@MinVersion(20)
public class RayVisualizerCommand extends SWCommand implements Listener {
private class CRayData {
private CRay[] rays = new CRay[27 * 400];
private boolean[] used = new boolean[27 * 400];
public void reset() {
for (int i = 0; i < 27 * 400; i++) {
if (!used[i] && rays[i] != null) {
rays[i].die();
rays[i] = null;
}
used[i] = false;
}
}
private boolean locEquals(Location loc1, Location loc2) {
if ((long)(loc1.getX() * 1000) != (long)(loc2.getX() * 1000)) return false;
if ((long)(loc1.getY() * 1000) != (long)(loc2.getY() * 1000)) return false;
if ((long)(loc1.getZ() * 1000) != (long)(loc2.getZ() * 1000)) return false;
return true;
}
public CRay get(Location from, Location to) {
for (int i = 0; i < rays.length; i++) {
if (rays[i] != null && locEquals(from, rays[i].getFrom()) && locEquals(to, rays[i].getTo())) {
used[i] = true;
return rays[i];
}
}
for (int i = 0; i < rays.length; i++) {
if (rays[i] != null && locEquals(from, rays[i].getFrom()) && !used[i]) {
used[i] = true;
return rays[i];
}
}
for (int i = 0; i < rays.length; i++) {
if (used[i]) continue;
CRay ray = rays[i];
if (ray != null) {
return ray;
}
ray = new CRay(server);
ray.setBlock(Material.LIME_CONCRETE.createBlockData());
ray.setWidth(1 / 32f);
rays[i] = ray;
used[i] = true;
return ray;
}
return null;
}
}
private CRayData rayData = new CRayData();
private final REntityServer server = new REntityServer();
private final World WORLD = Bukkit.getWorlds().get(0);
public RayVisualizerCommand() {
super("rayvisualizer");
Bukkit.getScheduler().runTaskTimer(BauSystem.getInstance(), () -> {
if (server.getPlayers().isEmpty()) return;
Map<Integer, List<TNTPrimed>> primedList = WORLD.getEntitiesByClass(TNTPrimed.class)
.stream()
.collect(Collectors.groupingBy(TNTPrimed::getFuseTicks));
rayData.reset();
if (primedList.isEmpty()) return;
List<Integer> fuseTicks = primedList.keySet().stream().sorted().collect(Collectors.toList());
List<TNTPrimed> current = new ArrayList<>();
for (int i = 0; i < fuseTicks.size(); i++) {
List<TNTPrimed> tnts = primedList.get(fuseTicks.get(i));
calculateRays(current, tnts);
current.addAll(tnts);
}
}, 1, 1);
}
private void calculateRays(List<TNTPrimed> fromTNTs, List<TNTPrimed> toTNTs) {
for (TNTPrimed from : fromTNTs) {
if (!from.isInWater()) continue;
for (TNTPrimed to : toTNTs) {
if (from == to) continue;
if (to.getLocation().distanceSquared(from.getLocation()) > 25) continue;
Location fromLoc = from.getLocation();
Location toLoc = to.getLocation().clone().add(-0.49, 0, -0.49);
final double minX = 0.5 * (1 - Math.floor(2 * 0.98 + 1) / (2 * 0.98 + 1));
final double minZ = 0.5 * (1 - Math.floor(2 * 0.98 + 1) / (2 * 0.98 + 1));
final double spacing = 0.98 / (2 * 0.98 + 1);
for (int dx = 0; dx < 3; dx++) {
for (int dy = 0; dy < 3; dy++) {
for (int dz = 0; dz < 3; dz++) {
Location end = toLoc.clone().add(minX + dx * spacing, 0 + dy * spacing, minZ + dz * spacing);
RayTraceResult result = fromLoc.getWorld().rayTraceBlocks(fromLoc, end.clone().subtract(fromLoc).toVector(), end.distance(fromLoc), FluidCollisionMode.NEVER, true);
if (result != null && result.getHitBlock() != null) {
continue;
}
CRay cRay = rayData.get(fromLoc, end);
if (cRay == null) continue;
cRay.setFrom(fromLoc);
cRay.setTo(end);
}
}
}
}
}
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
boolean rayvisualizer = Config.getInstance().get(event.getPlayer()).getPlainValueOrDefault("rayvisualizer", false);
if (rayvisualizer) server.addPlayer(event.getPlayer());
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
server.removePlayer(event.getPlayer());
}
@EventHandler
public void onBauMemberUpdate(BauMemberUpdateEvent event) {
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
for (Player player : Bukkit.getOnlinePlayers()) {
if (Permission.BUILD.hasPermission(player)) {
boolean rayvisualizer = Config.getInstance().get(player).getPlainValueOrDefault("rayvisualizer", false);
if (rayvisualizer) server.addPlayer(player);
} else {
server.removePlayer(player);
}
}
}, 1);
}
@Register
public void toggle(@Validator Player player) {
boolean rayvisualizer = Config.getInstance().get(player).getPlainValueOrDefault("rayvisualizer", false);
Config.getInstance().get(player).put("rayvisualizer", !rayvisualizer);
if (!rayvisualizer) {
SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("RAY_VISUALIZER_ENABLE", player));
server.addPlayer(player);
} else {
SWUtils.sendToActionbar(player, BauSystem.MESSAGE.parse("RAY_VISUALIZER_DISABLE", player));
server.removePlayer(player);
}
}
}

View File

@@ -34,8 +34,6 @@ import de.steamwar.linkage.Linked;
import de.steamwar.linkage.LinkedInstance;
import org.bukkit.entity.Player;
import java.io.IOException;
@Linked
public class ColorCommand extends SWCommand {
@@ -47,7 +45,7 @@ public class ColorCommand extends SWCommand {
}
@Register(description = "REGION_COLOR_HELP_COLOR")
public void genericColor(Player p, ColorMode color) {
public void genericColor(@Validator Player p, ColorMode color) {
genericColorSet(p, color, ColorizationType.LOCAL);
}
@@ -78,7 +76,7 @@ public class ColorCommand extends SWCommand {
}
@Register
public void genericColorSet(Player p, ColorizationType colorizationType, ColorMode color) {
public void genericColorSet(@Validator Player p, ColorizationType colorizationType, ColorMode color) {
genericColorSet(p, color, colorizationType);
}

View File

@@ -21,13 +21,9 @@ package de.steamwar.bausystem.features.util;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.Permission;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import de.steamwar.entity.*;
import de.steamwar.linkage.Linked;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import net.md_5.bungee.api.ChatMessageType;
import de.steamwar.linkage.MinVersion;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -37,101 +33,320 @@ import org.bukkit.block.PistonMoveReaction;
import org.bukkit.block.TileState;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.type.Piston;
import org.bukkit.entity.Display;
import org.bukkit.entity.Player;
import org.bukkit.entity.TextDisplay;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.*;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
@Linked
@MinVersion(20)
public class PistonCalculator implements Listener {
private final Map<Player, Long> DEBOUNCE = new HashMap<>();
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
if (!Permission.BUILD.hasPermission(event.getPlayer())) return;
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) return;
if (!event.hasItem() || event.getItem().getType() != Material.SLIME_BALL) return;
if (event.getItem() == null) {}
else if (event.getItem() != null && event.getItem().getType() == Material.SLIME_BALL) {}
else if (!event.getItem().getType().isBlock()) {
DEBOUNCE.put(event.getPlayer(), System.currentTimeMillis());
return;
}
else return;
if (event.getClickedBlock() == null) return;
Block clickedBlock = event.getClickedBlock();
Material blockType = clickedBlock.getType();
if (!(blockType == Material.PISTON || blockType == Material.STICKY_PISTON)) return;
Piston piston = (Piston) clickedBlock.getBlockData();
if (System.currentTimeMillis() - DEBOUNCE.getOrDefault(event.getPlayer(), 0L) <= 200) return;
DEBOUNCE.put(event.getPlayer(), System.currentTimeMillis());
if (blockType == Material.PISTON && piston.isExtended()) {
BauSystem.MESSAGE.sendPrefixless("PISTON_INFO", event.getPlayer(), ChatMessageType.ACTION_BAR, "§a", 0);
Location location = event.getClickedBlock().getLocation();
if (pistOrders.containsKey(location)) {
PistOrder pistOrder = pistOrders.get(location);
if (pistOrder.server.getPlayers().contains(event.getPlayer())) {
pistOrder.server.removePlayer(event.getPlayer());
} else {
pistOrder.server.addPlayer(event.getPlayer());
}
if (pistOrder.server.getPlayers().isEmpty()) {
pistOrders.remove(location);
pistOrder.server.close();
}
return;
}
boolean pulling = blockType == Material.STICKY_PISTON && (clickedBlock.getRelative(piston.getFacing()).getType() == Material.AIR || piston.isExtended());
PistOrder pistOrder = new PistOrder(clickedBlock);
pistOrder.calculate();
pistOrder.server.addPlayer(event.getPlayer());
pistOrders.put(location, pistOrder);
}
CalculationResult result = calc(clickedBlock, piston.getFacing(), (pulling ? piston.getFacing().getOppositeFace() : piston.getFacing()));
result.entityServer.addPlayer(event.getPlayer());
BauSystem.MESSAGE.sendPrefixless("PISTON_INFO", event.getPlayer(), ChatMessageType.ACTION_BAR, result.unmovable ? "§c" : (result.tooMany ? "§e" : "§a"), result.amount);
@EventHandler
public void onBlockPistonExtend(BlockPistonExtendEvent event) {
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
movePistOrders(event.getDirection(), event.getBlocks());
pistOrders.values().forEach(PistOrder::calculate);
}, 3);
}
@EventHandler
public void onBlockPistonRetract(BlockPistonRetractEvent event) {
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
movePistOrders(event.getDirection(), event.getBlocks());
pistOrders.values().forEach(PistOrder::calculate);
}, 3);
}
private void movePistOrders(BlockFace direction, List<Block> blocks) {
Set<PistOrder> orders = new HashSet<>();
blocks.forEach(block -> {
PistOrder pistOrder = pistOrders.get(block.getLocation());
if (pistOrder == null) return;
pistOrders.remove(block.getLocation());
pistOrder.piston = pistOrder.piston.getRelative(direction);
orders.add(pistOrder);
});
orders.forEach(pistOrder -> {
pistOrders.put(pistOrder.piston.getLocation(), pistOrder);
});
}
@EventHandler
public void onBlockPlace(BlockPlaceEvent event) {
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
pistOrders.values().forEach(PistOrder::calculate);
}, 1);
}
@EventHandler
public void onBlockBreak(BlockBreakEvent event) {
if (pistOrders.containsKey(event.getBlock().getLocation())) {
PistOrder pistOrder = pistOrders.remove(event.getBlock().getLocation());
pistOrder.server.close();
}
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
pistOrders.values().forEach(PistOrder::calculate);
}, 1);
}
@EventHandler
public void onPlayerQuit(PlayerQuitEvent event) {
DEBOUNCE.remove(event.getPlayer());
Set<Location> toRemove = new HashSet<>();
pistOrders.forEach((location, pistOrder) -> {
pistOrder.server.removePlayer(event.getPlayer());
if (pistOrder.server.getPlayers().isEmpty()) {
toRemove.add(location);
pistOrder.server.close();
}
});
toRemove.forEach(pistOrders::remove);
}
private final BlockFace[] FACES = new BlockFace[]{BlockFace.UP, BlockFace.DOWN, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST};
private CalculationResult calc(Block origin, BlockFace facing, BlockFace direction) {
Set<Block> blockSet = new HashSet<>();
Set<Location> unmovable = new HashSet<>();
private final Map<Location, PistOrder> pistOrders = new HashMap<>();
Block calcOrigin = origin;
if (facing != direction) calcOrigin = origin.getRelative(facing, 3);
private final class PistOrder {
private Block piston;
private REntityServer server = new REntityServer();
List<Block> toCalc = new LinkedList<>();
calcDirection(origin, null, calcOrigin, facing != direction ? origin.getRelative(facing) : null, facing, direction, blockSet, toCalc, unmovable);
private boolean pulling = false;
private List<Location> movedBlocks = new ArrayList<>();
private List<Location> brokenBlocks = new ArrayList<>();
private Set<Location> immovableBlocks = new HashSet<>();
while (!toCalc.isEmpty()) {
Block current = toCalc.remove(0);
blockSet.add(current);
public PistOrder(Block piston) {
this.piston = piston;
}
Material type = current.getType();
if (type != Material.SLIME_BLOCK && type != Material.HONEY_BLOCK) continue;
Material oppositeType = type == Material.SLIME_BLOCK ? Material.HONEY_BLOCK : Material.SLIME_BLOCK;
public void calculate() {
movedBlocks.clear();
brokenBlocks.clear();
immovableBlocks.clear();
for (BlockFace face : FACES) {
Block block = current.getRelative(face);
if (block.getType().isAir()) continue;
if (!isPiston(block) && (block.getPistonMoveReaction() == PistonMoveReaction.BLOCK || block.getPistonMoveReaction() == PistonMoveReaction.IGNORE || block.getPistonMoveReaction() == PistonMoveReaction.PUSH_ONLY || block.getState() instanceof TileState || block.getPistonMoveReaction() == PistonMoveReaction.BREAK)) continue;
if (block.getType() != oppositeType) {
if (!blockSet.contains(block)) toCalc.add(block);
calcDirection(null, origin, block, null, facing, direction, blockSet, toCalc, unmovable);
if (piston.isEmpty()) {
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
server.close();
pistOrders.remove(piston.getLocation());
}, 0);
return;
}
BlockData blockData = piston.getBlockData();
if (!(blockData instanceof Piston)) return;
Piston pistonData = (Piston) blockData;
if (piston.getType() == Material.PISTON && pistonData.isExtended()) {
visualize();
return;
}
pulling = piston.getType() == Material.STICKY_PISTON && (piston.getRelative(pistonData.getFacing()).getType() == Material.AIR || pistonData.isExtended());
calculate(piston, pistonData.getFacing(), (pulling ? pistonData.getFacing().getOppositeFace() : pistonData.getFacing()));
Collections.reverse(movedBlocks);
Collections.reverse(brokenBlocks);
visualize();
}
private void calculate(Block origin, BlockFace facing, BlockFace direction) {
Block calcOrigin = origin;
if (facing != direction) calcOrigin = origin.getRelative(facing, 3);
List<Block> toCalc = new LinkedList<>();
calcDirection(origin, null, calcOrigin, facing != direction ? origin.getRelative(facing) : null, facing, direction, toCalc);
while (!toCalc.isEmpty()) {
Block current = toCalc.remove(0);
if (!movedBlocks.contains(current.getLocation())) {
movedBlocks.add(current.getLocation());
}
Material type = current.getType();
if (type != Material.SLIME_BLOCK && type != Material.HONEY_BLOCK) continue;
Material oppositeType = type == Material.SLIME_BLOCK ? Material.HONEY_BLOCK : Material.SLIME_BLOCK;
for (BlockFace face : FACES) {
Block block = current.getRelative(face);
if (block.getType().isAir()) continue;
if (isImmovable(block) || block.getPistonMoveReaction() == PistonMoveReaction.BREAK) continue;
if (block.getType() != oppositeType) {
if (!movedBlocks.contains(block.getLocation())) toCalc.add(block);
calcDirection(null, origin, block, null, facing, direction, toCalc);
}
}
}
movedBlocks.remove(origin.getLocation());
if (facing != direction) movedBlocks.remove(origin.getRelative(facing, 1).getLocation());
if (pulling) immovableBlocks.remove(origin.getRelative(facing).getLocation());
}
blockSet.remove(origin);
if (facing != direction) blockSet.remove(origin.getRelative(facing, 1));
REntityServer entityServer = new REntityServer();
for (Location loc : unmovable) {
RFallingBlockEntity rFallingBlockEntity = new RFallingBlockEntity(entityServer, loc.clone().add(0.5, 0, 0.5), Material.RED_STAINED_GLASS);
rFallingBlockEntity.setGlowing(true);
rFallingBlockEntity.setNoGravity(true);
rFallingBlockEntity.setInvisible(true);
private void calcDirection(Block origin, Block blockOrigin, Block calcOrigin, Block ignore, BlockFace facing, BlockFace direction, List<Block> toCalc) {
for (int i = 1; i < 24; i++) {
Block block = calcOrigin.getRelative(direction, i);
if (block.equals(ignore)) return;
if (block.getPistonMoveReaction() == PistonMoveReaction.BREAK) {
if (!brokenBlocks.contains(block.getLocation())) {
brokenBlocks.add(block.getLocation());
}
return;
}
if (isImmovable(block)) {
immovableBlocks.add(block.getLocation());
return;
}
if (block.getType().isAir()) return;
if (facing == direction && block.equals(blockOrigin)) {
immovableBlocks.add(block.getLocation());
return;
}
if (facing != direction && (block.equals(origin) || block.getRelative(facing.getOppositeFace()).equals(origin))) return;
if (!movedBlocks.contains(block.getLocation())) toCalc.add(block);
}
}
private void visualize() {
server.getEntities().forEach(REntity::die);
for (int i = 0; i < movedBlocks.size(); i++) {
Location location = movedBlocks.get(i);
int order = i + 1;
CCubedTextDisplay display = new CCubedTextDisplay(server, location);
display.setText("§e" + order);
display.setBackgroundColor(0);
display.setShadowed(false);
Set<BlockFace> toHide = Arrays.stream(FACES).filter(blockFace -> {
return movedBlocks.contains(location.clone().add(blockFace.getModX(), blockFace.getModY(), blockFace.getModZ()));
}).collect(Collectors.toSet());
display.hide(toHide);
}
for (int i = 0; i < brokenBlocks.size(); i++) {
Location location = brokenBlocks.get(i);
int order = i + 1;
RTextDisplay textDisplay = new RTextDisplay(server, location.clone().add(0.5, 0.4, 0.5));
textDisplay.setText("§c" + order);
textDisplay.setBillboard(Display.Billboard.CENTER);
textDisplay.setAlignment(TextDisplay.TextAlignment.CENTER);
textDisplay.setSeeThrough(true);
textDisplay.setBackgroundColor(0);
textDisplay.setShadowed(false);
textDisplay.setBrightness(new Display.Brightness(15, 15));
}
for (Location location : immovableBlocks) {
CWireframe wireframe = new CWireframe(server);
wireframe.setPos1(location);
wireframe.setPos2(location);
wireframe.setWidth(1 / 32f);
wireframe.setBlock(Material.RED_CONCRETE.createBlockData());
}
CWireframe wireframe = new CWireframe(server);
wireframe.setPos1(piston.getLocation());
wireframe.setPos2(piston.getLocation());
if (!immovableBlocks.isEmpty()) {
wireframe.setBlock(Material.RED_CONCRETE.createBlockData());
} else if (movedBlocks.size() > 12) {
wireframe.setBlock(Material.YELLOW_CONCRETE.createBlockData());
} else {
wireframe.setBlock(Material.LIME_CONCRETE.createBlockData());
}
wireframe.setWidth(1 / 32f);
RTextDisplay textDisplay = new RTextDisplay(server, piston.getLocation().clone().add(0.5, 0.3, 0.5));
StringBuilder text = new StringBuilder();
if (pulling) {
text.append("§ePull\n");
} else {
text.append("§ePush\n");
}
text.append("§f").append(movedBlocks.size()).append(" §eBlocks");
textDisplay.setText(text.toString());
textDisplay.setBillboard(Display.Billboard.CENTER);
textDisplay.setAlignment(TextDisplay.TextAlignment.CENTER);
textDisplay.setSeeThrough(true);
textDisplay.setBackgroundColor(0);
textDisplay.setShadowed(false);
textDisplay.setBrightness(new Display.Brightness(15, 15));
}
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), entityServer::close, 20);
return new CalculationResult(blockSet.size(), blockSet.size() > 12, !unmovable.isEmpty(), entityServer);
}
private void calcDirection(Block origin, Block blockOrigin, Block calcOrigin, Block ignore, BlockFace facing, BlockFace direction, Set<Block> blockSet, List<Block> toCalc, Set<Location> unmovable) {
for (int i = 1; i < 24; i++) {
Block block = calcOrigin.getRelative(direction, i);
if (block.equals(ignore)) return;
if (block.getPistonMoveReaction() == PistonMoveReaction.BREAK) return;
if (!isPiston(block) && (block.getPistonMoveReaction() == PistonMoveReaction.BLOCK || block.getPistonMoveReaction() == PistonMoveReaction.IGNORE || block.getState() instanceof TileState)) {
unmovable.add(block.getLocation());
return;
}
if (block.getType().isAir()) return;
if (facing == direction && block.equals(blockOrigin)) {
unmovable.add(block.getLocation());
return;
}
if (facing != direction && (block.equals(origin) || block.getRelative(facing.getOppositeFace()).equals(origin))) return;
if (!blockSet.contains(block)) toCalc.add(block);
private boolean isImmovable(Block block) {
if (block.isEmpty() || block.isLiquid()) {
return false;
}
BlockData blockData = block.getBlockData();
if (blockData instanceof Piston) {
return ((Piston) blockData).isExtended();
}
if (block.getState() instanceof TileState) {
return true;
}
PistonMoveReaction reaction = block.getPistonMoveReaction();
if (reaction == PistonMoveReaction.IGNORE) return true;
if (reaction == PistonMoveReaction.BLOCK) return true;
switch (block.getType()) {
case OBSIDIAN:
return true;
default:
return false;
}
}
@@ -142,13 +357,4 @@ public class PistonCalculator implements Listener {
}
return false;
}
@AllArgsConstructor
@Getter
private static class CalculationResult {
private int amount;
private boolean tooMany;
private boolean unmovable;
private REntityServer entityServer;
}
}

View File

@@ -22,9 +22,11 @@ package de.steamwar.bausystem.features.util;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.command.SWCommand;
import de.steamwar.linkage.Linked;
import de.steamwar.linkage.MinVersion;
import org.bukkit.entity.Player;
@Linked
@MinVersion(20)
public class PistonCalculatorCommand extends SWCommand {
public PistonCalculatorCommand() {

View File

@@ -0,0 +1,180 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.bausystem.features.worldedit;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.bausystem.region.Point;
import de.steamwar.bausystem.utils.FlatteningWrapper;
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.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitTask;
import org.bukkit.util.Vector;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
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),
};
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 class Selector {
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<>();
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(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);
}
}, 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);
}
}
}
}
}

View File

@@ -145,6 +145,13 @@ public class PasteBuilder {
public PasteBuilder removeWater(boolean removeWater) {
if (!removeWater) return this;
BaseBlock water = Objects.requireNonNull(BlockTypes.get("water")).getDefaultState().toBaseBlock();
BlockType bubble_column_type = BlockTypes.get("bubble_column");
BaseBlock bubble_column;
if (bubble_column_type == null) {
bubble_column = null;
} else {
bubble_column = bubble_column_type.getDefaultState().toBaseBlock();
}
BaseBlock air = Objects.requireNonNull(BlockTypes.get("air")).getDefaultState().toBaseBlock();
WaterloggedRemover waterloggedRemover = new WaterloggedRemover(getClipboard());
@@ -154,6 +161,10 @@ public class PasteBuilder {
clipboard.setBlock(blockVector3, air);
return;
}
if (bubble_column != null && baseBlock.equals(bubble_column)) {
clipboard.setBlock(blockVector3, air);
return;
}
String blockName = clipboard.getFullBlock(blockVector3).getBlockType().getName();
if (blockName.equals("Water")) {
clipboard.setBlock(blockVector3, air);

View File

@@ -0,0 +1,41 @@
/*
* 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.network.packets.client;
import de.steamwar.network.packets.NetworkPacket;
import lombok.*;
@EqualsAndHashCode(callSuper = true)
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Builder(toBuilder = true)
public class AnvilAnswerPacket extends NetworkPacket {
private int playerId;
private Action action;
private String text;
public enum Action {
CLOSE,
ANSWER,
LEFT_CLICK
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.network.packets.server;
import de.steamwar.network.packets.NetworkPacket;
import lombok.*;
@EqualsAndHashCode(callSuper = true)
@Getter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class AnvilInventoryPacket extends NetworkPacket {
private static final long serialVersionUID = -6004390311854048209L;
private int playerId;
private String title;
private String defaultText;
private String material;
}

View File

@@ -55,4 +55,7 @@ public class CraftbukkitWrapper10 implements CraftbukkitWrapper {
public Stream<?> entityIterator() {
return ((CraftWorld) Config.world).getHandle().entityList.stream();
}
@Override
public void setupGamerule() { }
}

View File

@@ -55,4 +55,7 @@ public class CraftbukkitWrapper12 implements CraftbukkitWrapper {
public Stream<?> entityIterator() {
return ((CraftWorld) Config.world).getHandle().entityList.stream();
}
@Override
public void setupGamerule() { }
}

View File

@@ -56,4 +56,7 @@ public class CraftbukkitWrapper14 implements CraftbukkitWrapper {
public Stream<?> entityIterator() {
return ((CraftWorld) Config.world).getHandle().entitiesById.values().stream();
}
@Override
public void setupGamerule() { }
}

View File

@@ -56,4 +56,7 @@ public class CraftbukkitWrapper15 implements CraftbukkitWrapper {
public Stream<?> entityIterator() {
return ((CraftWorld) Config.world).getHandle().entitiesById.values().stream();
}
@Override
public void setupGamerule() { }
}

View File

@@ -62,4 +62,7 @@ public class CraftbukkitWrapper18 implements CraftbukkitWrapper {
public Stream<?> entityIterator() {
return StreamSupport.stream(((Iterable<?>) getIterable.invoke(getWorldEntities.invoke(getWorld.invoke(Config.world)))).spliterator(), false);
}
@Override
public void setupGamerule() { }
}

View File

@@ -24,6 +24,7 @@ plugins {
dependencies {
compileOnly(project(":FightSystem:FightSystem_Core", "default"))
compileOnly(project(":FightSystem:FightSystem_18", "default"))
compileOnly(project(":SpigotCore", "default"))
compileOnly(libs.paperapi21) {
attributes {
@@ -40,4 +41,5 @@ dependencies {
}
compileOnly(libs.fastutil)
compileOnly(libs.authlib)
}

View File

@@ -0,0 +1,77 @@
/*
* 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.utils;
import com.comphenix.tinyprotocol.TinyProtocol;
import com.mojang.authlib.GameProfile;
import de.steamwar.core.ProtocolWrapper;
import de.steamwar.fightsystem.FightSystem;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.block.CraftBlockState;
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
public class BlockIdWrapper21 implements BlockIdWrapper {
@Override
public Material idToMaterial(int blockState) {
return CraftMagicNumbers.getMaterial(net.minecraft.world.level.block.Block.stateById(blockState)).getItemType();
}
@Override
public int blockToId(Block block) {
return net.minecraft.world.level.block.Block.getId(((CraftBlockState) block.getState()).getHandle());
}
@Override
public void setBlock(World world, int x, int y, int z, int blockState) {
BlockState blockData = net.minecraft.world.level.block.Block.stateById(blockState);
ServerLevel level = ((CraftWorld) world).getHandle();
BlockPos pos = new BlockPos(x, y, z);
level.removeBlockEntity(pos);
level.setBlock(pos, blockData, blockState);
level.getChunkSource().blockChanged(pos);
}
@Override
public void trackEntity(Player player, Entity entity) {
if(entity instanceof Player)
TinyProtocol.instance.sendPacket(player, ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.REMOVE, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE));
player.showEntity(FightSystem.getPlugin(), entity);
}
@Override
public void untrackEntity(Player player, Entity entity) {
player.hideEntity(FightSystem.getPlugin(), entity);
if(entity instanceof Player)
TinyProtocol.instance.sendPacket(player, ProtocolWrapper.impl.playerInfoPacketConstructor(ProtocolWrapper.PlayerInfoAction.ADD, new GameProfile(entity.getUniqueId(), entity.getName()), GameMode.CREATIVE));
}
}

View File

@@ -19,12 +19,43 @@
package de.steamwar.fightsystem.utils;
import de.steamwar.fightsystem.Config;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import org.bukkit.GameRule;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Entity;
import java.util.HashSet;
import java.util.Set;
public class CraftbukkitWrapper21 extends CraftbukkitWrapper18 {
@Override
public float headRotation(Entity e) {
return getEntity(e).getYHeadRot();
}
@Override
public void setupGamerule() {
Config.world.setGameRule(GameRule.LOCATOR_BAR, false);
}
private LevelChunk getChunk(World world, int x, int z) {
return ((CraftWorld) world).getHandle().getChunk(x, z);
}
@Override
public void resetChunk(World world, World backup, int x, int z) {
LevelChunk worldChunk = getChunk(world, x, z);
LevelChunk backupChunk = getChunk(backup, x, z);
LevelChunkSection[] sections = worldChunk.getSections();
System.arraycopy(backupChunk.getSections(), 0, sections, 0, sections.length);
Set<BlockPos> blocks = new HashSet<>(worldChunk.blockEntities.keySet());
blocks.stream().filter(key -> !backupChunk.blockEntities.containsKey(key)).forEach(worldChunk::removeBlockEntity);
worldChunk.heightmaps.clear();
worldChunk.heightmaps.putAll(backupChunk.heightmaps);
}
}

View File

@@ -0,0 +1,36 @@
/*
* 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.utils;
import io.papermc.paper.datacomponent.DataComponentTypes;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class ReflectionWrapper21 implements ReflectionWrapper {
@Override
public Object explosionHider(Player player, Object packet, PacketHiderFunction packetHiderFunction) {
return packet;
}
@Override
public boolean hasItems(ItemStack stack) {
return stack.getDataTypes().stream().anyMatch(dataComponentType -> dataComponentType != DataComponentTypes.ENCHANTMENTS || dataComponentType != DataComponentTypes.DAMAGE);
}
}

View File

@@ -52,4 +52,7 @@ public class CraftbukkitWrapper8 implements CraftbukkitWrapper {
public Stream<?> entityIterator() {
return ((CraftWorld) Config.world).getHandle().entityList.stream();
}
@Override
public void setupGamerule() { }
}

View File

@@ -0,0 +1,59 @@
/*
* 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.utils;
import de.steamwar.Reflection;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
public class ReflectionWrapper8 implements ReflectionWrapper {
private static final Class<?> packetPlayOutExplosion = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundExplodePacket");
private static final Reflection.Field<List> explosionBlocks = Reflection.getField(packetPlayOutExplosion, List.class, 0);
private static final Function<Object, Location> explosionLocation = HullHider.posPacketToLocation(packetPlayOutExplosion, double.class, 1.0);
@Override
public Object explosionHider(Player player, Object packet, PacketHiderFunction packetHiderFunction) {
if(explosionBlocks.get(packet).isEmpty())
return packetHiderFunction.hide(player, packet, explosionLocation.apply(packet));
return packet;
}
private static final Class<?> itemStack = Reflection.getClass("net.minecraft.world.item.ItemStack");
private static final Reflection.Method asNMSCopy = Reflection.getTypedMethod(Reflection.getClass("org.bukkit.craftbukkit.inventory.CraftItemStack"), "asNMSCopy", itemStack, ItemStack.class);
private static final Class<?> nbtTagCompound = Reflection.getClass("net.minecraft.nbt.CompoundTag");
private static final Reflection.Method getTag = Reflection.getTypedMethod(itemStack, null, nbtTagCompound);
private static final Reflection.Method getKeys = Reflection.getTypedMethod(nbtTagCompound, null, Set.class);
@Override
public boolean hasItems(ItemStack stack) {
Set<String> keys = new HashSet<>((Set<String>) getKeys.invoke(getTag.invoke(asNMSCopy.invoke(null, stack))));
keys.remove("Enchantments");
keys.remove("Damage");
return !keys.isEmpty();
}
}

View File

@@ -55,4 +55,7 @@ public class CraftbukkitWrapper9 implements CraftbukkitWrapper {
public Stream<?> entityIterator() {
return ((CraftWorld) Config.world).getHandle().entityList.stream();
}
@Override
public void setupGamerule() { }
}

View File

@@ -188,6 +188,8 @@ public class FightSystem extends JavaPlugin {
}else if(Config.mode == ArenaMode.PREPARE) {
Fight.getUnrotated().setSchem(SchematicNode.getSchematicNode(Config.PrepareSchemID));
}
CraftbukkitWrapper.impl.setupGamerule();
}
@Override

View File

@@ -26,6 +26,7 @@ import de.steamwar.fightsystem.commands.Commands;
import de.steamwar.fightsystem.commands.GUI;
import de.steamwar.fightsystem.listener.PersonalKitCreator;
import de.steamwar.fightsystem.utils.FlatteningWrapper;
import de.steamwar.fightsystem.utils.ReflectionWrapper;
import de.steamwar.inventory.SWInventory;
import de.steamwar.inventory.SWItem;
import de.steamwar.sql.PersonalKit;
@@ -214,17 +215,8 @@ public class Kit {
assert normal != null;
return !normal.isEnchantmentInKit(stack) && !stack.getEnchantments().isEmpty();
}
private static final Class<?> itemStack = Reflection.getClass("net.minecraft.world.item.ItemStack");
private static final Reflection.Method asNMSCopy = Reflection.getTypedMethod(Reflection.getClass("org.bukkit.craftbukkit.inventory.CraftItemStack"), "asNMSCopy", itemStack, ItemStack.class);
private static final Class<?> nbtTagCompound = Reflection.getClass("net.minecraft.nbt.CompoundTag");
private static final Reflection.Method getTag = Reflection.getTypedMethod(itemStack, null, nbtTagCompound);
private static final Reflection.Method getKeys = Reflection.getTypedMethod(nbtTagCompound, null, Set.class);
public static boolean hasItems(ItemStack stack) {
Set<String> keys = new HashSet<>((Set<String>) getKeys.invoke(getTag.invoke(asNMSCopy.invoke(null, stack))));
keys.remove("Enchantments");
keys.remove("Damage");
return !keys.isEmpty();
return ReflectionWrapper.impl.hasItems(stack);
}
private boolean isEnchantmentInKit(ItemStack stack){

View File

@@ -33,4 +33,6 @@ public interface CraftbukkitWrapper {
float headRotation(Entity e);
Stream<?> entityIterator();
void setupGamerule();
}

View File

@@ -62,6 +62,7 @@ public class HullHider implements Listener {
private final Hull[] hulls;
private final Map<Class<?>, BiFunction<Player, Object, Object>> packetHiders = new HashMap<>();
private static final Class<?> packetPlayOutExplosion = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundExplodePacket");
public HullHider() {
if(!TechHiderWrapper.ENABLED) {
hulls = new Hull[0];
@@ -208,14 +209,8 @@ public class HullHider implements Listener {
return packetHider(player, packet, new Location(Config.world, TechHider.blockPositionX.get(baseBlock), blockPositionY.get(baseBlock), TechHider.blockPositionZ.get(baseBlock)));
}
private static final Class<?> packetPlayOutExplosion = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundExplodePacket");
private static final Reflection.Field<List> explosionBlocks = Reflection.getField(packetPlayOutExplosion, List.class, 0);
private static final Function<Object, Location> explosionLocation = posPacketToLocation(packetPlayOutExplosion, double.class, 1.0);
private Object explosionHider(Player player, Object packet) {
if(explosionBlocks.get(packet).isEmpty())
return packetHider(player, packet, explosionLocation.apply(packet));
return packet;
return ReflectionWrapper.impl.explosionHider(player, packet, this::packetHider);
}
private void posHiderGenerator(String typeName, Class<? extends Number> posType, double factor) {
@@ -224,7 +219,7 @@ public class HullHider implements Listener {
packetHiders.put(type, (player, packet) -> packetHider(player, packet, location.apply(packet)));
}
private static Function<Object, Location> posPacketToLocation(Class<?> type, Class<? extends Number> posType, double factor) {
public static Function<Object, Location> posPacketToLocation(Class<?> type, Class<? extends Number> posType, double factor) {
Reflection.Field<? extends Number> x = Reflection.getField(type, posType, 0);
Reflection.Field<? extends Number> y = Reflection.getField(type, posType, 1);
Reflection.Field<? extends Number> z = Reflection.getField(type, posType, 2);

View File

@@ -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/>.
*/
package de.steamwar.fightsystem.utils;
import de.steamwar.core.VersionDependent;
import de.steamwar.fightsystem.FightSystem;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public interface ReflectionWrapper {
ReflectionWrapper impl = VersionDependent.getVersionImpl(FightSystem.getPlugin());
Object explosionHider(Player player, Object packet, PacketHiderFunction packetHiderFunction);
boolean hasItems(ItemStack stack);
public interface PacketHiderFunction {
Object hide(Player player, Object packet, Location location);
}
}

View File

@@ -8,7 +8,7 @@ softdepend:
- SpigotCore
depend:
- WorldEdit
api-version: "1.13"
api-version: "1.21.6"
commands:
ak:

View File

@@ -49,3 +49,24 @@ tasks.register<FightServer>("WarGear20") {
worldName = "arenas/Pentraki"
config = "WarGear20.yml"
}
tasks.register<FightServer>("WarGear21") {
group = "run"
description = "Run a WarGear 1.21 Fight Server"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":FightSystem:shadowJar")
template = "WarGear21"
worldName = "arenas/Pentraki"
config = "WarGear20.yml"
jar = "/jars/paper-1.21.6.jar"
}
tasks.register<FightServer>("SpaceCraftDev20") {
group = "run"
description = "Run a SpaceCraftDev 1.20 Fight Server"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":FightSystem:shadowJar")
template = "SpaceCraft20"
worldName = "arenas/AS_Horizon"
config = "SpaceCraftDev20.yml"
}

View File

@@ -27,7 +27,6 @@ import de.steamwar.providers.BauServerInfo;
import de.steamwar.schematicsystem.SchematicSystem;
import de.steamwar.schematicsystem.commands.schematiccommand.SchematicCommand;
import de.steamwar.sql.*;
import net.md_5.bungee.api.chat.ClickEvent;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
@@ -44,7 +43,6 @@ public class SavePart extends SWCommand {
}
@Register("save")
@Register("s")
public void saveSchem(Player player) {
SchematicSelector selector = new SchematicSelector(player, SchematicSelector.selectSchematicNode(), schematicNode -> {
if(schematicNode == null || schematicNode.isDir()) {
@@ -61,7 +59,6 @@ public class SavePart extends SWCommand {
}
@Register("save")
@Register("s")
public void saveSchem(Player player, @AbstractSWCommand.Mapper("stringMapper") String name) {
SteamwarUser user = getUser(player);
if(BauServerInfo.isBauServer() && BauServerInfo.getOwnerId() != user.getId() &&

View File

@@ -31,19 +31,23 @@ public class CRIUSupport {
throw new UnsupportedOperationException("This is a Dummy");
}
public void setAutoDedup(boolean autoDedup) {
public CRIUSupport setGhostFileLimit(long limit) {
throw new UnsupportedOperationException("This is a Dummy");
}
public void setShellJob(boolean shellJob) {
public CRIUSupport setAutoDedup(boolean autoDedup) {
throw new UnsupportedOperationException("This is a Dummy");
}
public void setFileLocks(boolean fileLocks) {
public CRIUSupport setShellJob(boolean shellJob) {
throw new UnsupportedOperationException("This is a Dummy");
}
public void setLogFile(String logFile) {
public CRIUSupport setFileLocks(boolean fileLocks) {
throw new UnsupportedOperationException("This is a Dummy");
}
public CRIUSupport setLogFile(String logFile) {
throw new UnsupportedOperationException("This is a Dummy");
}

View File

@@ -19,5 +19,8 @@
package org.eclipse.openj9.criu;
import lombok.experimental.StandardException;
@StandardException
public class JVMCRIUException extends Exception {
}

View File

@@ -37,4 +37,7 @@ dependencies {
compileOnly(libs.paperapi21)
compileOnly(libs.nms21)
compileOnly(libs.datafixer)
compileOnly(libs.netty)
compileOnly(libs.authlib)
}

View File

@@ -0,0 +1,66 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2022 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.core;
import com.mojang.authlib.GameProfile;
import com.mojang.datafixers.util.Pair;
import de.steamwar.Reflection;
import net.minecraft.Util;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket;
import net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket;
import net.minecraft.world.level.GameType;
import org.bukkit.GameMode;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.function.LongSupplier;
public class ProtocolWrapper21 implements ProtocolWrapper {
private static final Reflection.Field<List> equipmentStack = Reflection.getField(equipmentPacket, List.class, 0);
@Override
public void setEquipmentPacketStack(Object packet, Object slot, Object stack) {
equipmentStack.set(packet, Collections.singletonList(new Pair<>(slot, stack)));
}
private static final Reflection.Constructor removePacketConstructor = Reflection.getConstructor(ClientboundPlayerInfoRemovePacket.class, List.class);
@Override
@SuppressWarnings("deprecation")
public Object playerInfoPacketConstructor(PlayerInfoAction action, GameProfile profile, GameMode mode) {
if(action == PlayerInfoAction.REMOVE)
return removePacketConstructor.invoke(Collections.singletonList(profile.getId()));
return switch (action) {
case ADD -> new ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.ADD_PLAYER, ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE), new ClientboundPlayerInfoUpdatePacket.Entry(
profile.getId(), profile, true, 0, GameType.byId(mode.getValue()), null, true, 0, null
));
case GAMEMODE -> new ClientboundPlayerInfoUpdatePacket(EnumSet.of(ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE), new ClientboundPlayerInfoUpdatePacket.Entry(
profile.getId(), profile, true, 0, GameType.byId(mode.getValue()), null, true, 0, null
));
default -> null;
};
}
@Override
public void initTPSWarp(LongSupplier longSupplier) {
Util.timeSource = () -> System.nanoTime() + longSupplier.getAsLong();
}
}

View File

@@ -0,0 +1,157 @@
/*
* 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 io.netty.buffer.ByteBuf;
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.entity.BlockEntityType;
import org.bukkit.entity.Player;
import java.util.List;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
public class ChunkHider21 implements ChunkHider {
@Override
public Class<?> mapChunkPacket() {
return ClientboundLevelChunkWithLightPacket.class;
}
private static final UnaryOperator<Object> chunkPacketCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class);
private static final UnaryOperator<Object> chunkDataCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class);
private static final Reflection.Field<Integer> chunkXField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 0);
private static final Reflection.Field<Integer> chunkZField = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, int.class, 1);
private static final Reflection.Field<ClientboundLevelChunkPacketData> chunkData = Reflection.getField(ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkPacketData.class, 0);
private static final Reflection.Field<byte[]> dataField = Reflection.getField(ClientboundLevelChunkPacketData.class, byte[].class, 0);
private static final Reflection.Field<List> tileEntities = Reflection.getField(ClientboundLevelChunkPacketData.class, List.class, 0);
@Override
public BiFunction<Player, Object, Object> chunkHiderGenerator(TechHider techHider) {
return (p, packet) -> {
int chunkX = chunkXField.get(packet);
int chunkZ = chunkZField.get(packet);
if (techHider.getLocationEvaluator().skipChunk(p, chunkX, chunkZ))
return packet;
packet = chunkPacketCloner.apply(packet);
Object dataWrapper = chunkDataCloner.apply(chunkData.get(packet));
Set<String> hiddenBlockEntities = techHider.getHiddenBlockEntities();
tileEntities.set(dataWrapper, ((List<?>)tileEntities.get(dataWrapper)).stream().filter(te -> tileEntityVisible(hiddenBlockEntities, te)).collect(Collectors.toList()));
ByteBuf in = Unpooled.wrappedBuffer(dataField.get(dataWrapper));
ByteBuf out = Unpooled.buffer(in.readableBytes() + 64);
for(int yOffset = p.getWorld().getMinHeight(); yOffset < p.getWorld().getMaxHeight(); yOffset += 16) {
SectionHider section = new SectionHider(p, techHider, in, out, chunkX, yOffset/16, chunkZ);
section.copyBlockCount();
blocks(section);
biomes(section);
}
if (in.readableBytes() != 0) {
throw new IllegalStateException("ChunkHider21: Incomplete chunk data, " + in.readableBytes() + " bytes left");
}
byte[] data = new byte[out.readableBytes()];
out.readBytes(data);
dataField.set(dataWrapper, data);
chunkData.set(packet, dataWrapper);
return packet;
};
}
public static final Class<?> tileEntity = Reflection.getClass("net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData$BlockEntityInfo");
protected static final Reflection.Field<BlockEntityType> entityType = Reflection.getField(tileEntity, BlockEntityType.class, 0);
private static final Class<?> builtInRegestries = Reflection.getClass("net.minecraft.core.registries.BuiltInRegistries");
private static final Class<?> registry = Reflection.getClass("net.minecraft.core.Registry");
private static final Reflection.Field<?> nameField = Reflection.getField(builtInRegestries, "BLOCK_ENTITY_TYPE", registry);
private static final Class<?> resourceLocation = Reflection.getClass("net.minecraft.resources.ResourceLocation");
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))));
}
private void blocks(SectionHider section) {
section.copyBitsPerBlock();
boolean singleValued = section.getBitsPerBlock() == 0;
if (singleValued) {
int value = ProtocolUtils.readVarInt(section.getIn());
ProtocolUtils.writeVarInt(section.getOut(), !section.isSkipSection() && section.getObfuscate().contains(value) ? section.getTarget() : value);
return;
} else {
section.processPalette();
}
if (section.isSkipSection() || (!section.blockPrecise() && section.isPaletted())) {
section.skipNewDataArray(4096);
return;
}
SimpleBitStorage values = new SimpleBitStorage(section.getBitsPerBlock(), 4096, section.readNewDataArray(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;
TechHider.State test = section.test(x, y, z);
switch (test) {
case SKIP:
break;
case CHECK:
if (!section.getObfuscate().contains(values.get(pos)))
break;
case HIDE:
values.set(pos, section.getTarget());
break;
case HIDE_AIR:
default:
values.set(pos, section.getAir());
}
}
}
}
section.writeDataArray(values.getRaw());
}
private void biomes(SectionHider section) {
section.copyBitsPerBlock();
if(section.getBitsPerBlock() == 0) {
section.copyVarInt();
} else if(section.getBitsPerBlock() < 6) {
section.skipPalette();
section.skipNewDataArray(64);
}
}
}

View File

@@ -157,7 +157,23 @@ public final class Reflection {
} else if(MAJOR_VERSION < 21 || MINOR_VERSION < 4) {
return Class.forName(spigotClassnames.getOrDefault(name, name));
} else {
return Class.forName(name);
Class<?> clazz = null;
try {
clazz = Class.forName(name);
} catch (ClassNotFoundException e) {}
if (clazz != null && clazz.getName().equals(name)) {
return clazz;
}
try {
return Core.class.getClassLoader().getParent().loadClass(name);
} catch (ClassNotFoundException e) {
if (clazz == null) {
throw e;
}
return clazz;
}
}
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Cannot find " + name, e);

View File

@@ -19,8 +19,8 @@
package de.steamwar.core;
import de.steamwar.Reflection;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.sql.internal.Statement;
import io.netty.channel.ChannelFuture;
import org.bukkit.Bukkit;
@@ -112,6 +112,7 @@ class CheckpointUtilsJ9 {
// Do the checkpoint
path.toFile().mkdirs();
CRIUSupport criu = new CRIUSupport(path);
criu.setGhostFileLimit(10L * 1024L * 1024L);
criu.setAutoDedup(true);
criu.setFileLocks(true);
criu.setShellJob(true);

View File

@@ -39,11 +39,14 @@ import org.bukkit.util.Vector;
public class WorldEditRenderer implements Listener {
private static WorldEditRenderer INSTANCE;
private static final Material WAND = FlatteningWrapper.impl.getMaterial("WOOD_AXE");
private final WorldEditPlugin we;
public WorldEditRenderer() {
INSTANCE = this;
we = WorldEditWrapper.getWorldEditPlugin();
Bukkit.getPluginManager().registerEvents(this, Core.getInstance());
@@ -54,6 +57,10 @@ public class WorldEditRenderer implements Listener {
}, 20, 20);
}
public static void renderPlayer(Player player) {
WorldEditRenderer.INSTANCE.renderPlayer(player, false);
}
private void renderPlayer(Player player, boolean scheduled) {
LocalSession session = we.getSession(player);
renderClipboard(player, session, scheduled);

View File

@@ -0,0 +1,127 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.entity;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.util.Transformation;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class CCubedTextDisplay extends CEntity {
private Map<BlockFace, RTextDisplay> displays = new HashMap<>();
public CCubedTextDisplay(REntityServer server, Location loc) {
super(server);
{
Location location = loc.clone().add(0.5, 0.4, 1.01);
RTextDisplay display = new RTextDisplay(server, location);
entities.add(display);
displays.put(BlockFace.SOUTH, display);
}
{
Location location = loc.clone().add(0.5, 0.4, -0.01);
RTextDisplay display = new RTextDisplay(server, location);
display.setTransform(new Transformation(new Vector3f(0, 0, 0), new Quaternionf().rotateY((float) Math.toRadians(180)), new Vector3f(1, 1, 1), new Quaternionf()));
entities.add(display);
displays.put(BlockFace.NORTH, display);
}
{
Location location = loc.clone().add(1.01, 0.4, 0.5);
RTextDisplay display = new RTextDisplay(server, location);
display.setTransform(new Transformation(new Vector3f(0, 0, 0), new Quaternionf().rotateY((float) Math.toRadians(90)), new Vector3f(1, 1, 1), new Quaternionf()));
entities.add(display);
displays.put(BlockFace.EAST, display);
}
{
Location location = loc.clone().add(-0.01, 0.4, 0.5);
RTextDisplay display = new RTextDisplay(server, location);
display.setTransform(new Transformation(new Vector3f(0, 0, 0), new Quaternionf().rotateY((float) Math.toRadians(270)), new Vector3f(1, 1, 1), new Quaternionf()));
entities.add(display);
displays.put(BlockFace.WEST, display);
}
{
Location location = loc.clone().add(0.5, 1.01, 0.6);
RTextDisplay display = new RTextDisplay(server, location);
display.setTransform(new Transformation(new Vector3f(0, 0, 0), new Quaternionf().rotationX((float) Math.toRadians(270)), new Vector3f(1, 1, 1), new Quaternionf()));
entities.add(display);
displays.put(BlockFace.UP, display);
}
{
Location location = loc.clone().add(0.5, -0.01, 0.4);
RTextDisplay display = new RTextDisplay(server, location);
display.setTransform(new Transformation(new Vector3f(0, 0, 0), new Quaternionf().rotationX((float) Math.toRadians(90)), new Vector3f(1, 1, 1), new Quaternionf()));
entities.add(display);
displays.put(BlockFace.DOWN, display);
}
}
public void hide(Set<BlockFace> blockFaceSet) {
for (BlockFace blockFace : BlockFace.values()) {
RTextDisplay display = displays.get(blockFace);
if (display == null) continue;
display.hide(blockFaceSet.contains(blockFace));
}
if (displays.values().stream().allMatch(RTextDisplay::isHidden)) {
displays.values().forEach(rTextDisplay -> rTextDisplay.hide(false));
}
}
@Override
public void move(Location location) {
}
@Override
public void move(double locX, double locY, double locZ, float pitch, float yaw, byte headYaw) {
}
public void setText(String text) {
displays.values().forEach(rTextDisplay -> rTextDisplay.setText(text));
}
public void setLineWidth(int lineWidth) {
displays.values().forEach(rTextDisplay -> rTextDisplay.setLineWidth(lineWidth));
}
public void setTextOpacity(byte textOpacity) {
displays.values().forEach(rTextDisplay -> rTextDisplay.setTextOpacity(textOpacity));
}
public void setShadowed(boolean shadowed) {
displays.values().forEach(rTextDisplay -> rTextDisplay.setShadowed(shadowed));
}
public void setSeeThrough(boolean seeThrough) {
displays.values().forEach(rTextDisplay -> rTextDisplay.setSeeThrough(seeThrough));
}
public void setBackgroundColor(int color) {
displays.values().forEach(rTextDisplay -> rTextDisplay.setBackgroundColor(color));
}
public void setDefaultBackground(boolean defaultBackground) {
displays.values().forEach(rTextDisplay -> rTextDisplay.setDefaultBackground(defaultBackground));
}
}

View File

@@ -19,6 +19,7 @@
package de.steamwar.entity;
import lombok.Getter;
import org.bukkit.Location;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Display;
@@ -31,6 +32,10 @@ import org.joml.Vector3f;
import java.util.Objects;
/**
* Can be used for Axis Aligned Lines of near infinite length.
*/
@Getter
public class CLine extends CEntity {
public static final float DEFAULT_WIDTH = 1 / 16f;

View File

@@ -0,0 +1,131 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.entity;
import lombok.Getter;
import org.bukkit.Location;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Display;
import org.bukkit.util.Consumer;
import org.bukkit.util.Transformation;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.Objects;
/**
* Can be used for Lines of short length.
*/
@Getter
public class CRay extends CEntity {
public static final float DEFAULT_WIDTH = 1 / 16f;
private Location from;
private Location to;
private float width = DEFAULT_WIDTH;
private BlockData blockData = RBlockDisplay.DEFAULT_BLOCK;
private boolean hide = false;
public CRay(REntityServer server) {
super(server);
}
private <T> CRay checkAndSet(T currentValue, T newValue, Consumer<T> setter) {
if (Objects.equals(currentValue, newValue)) return this;
setter.accept(newValue);
tick();
return this;
}
public CRay setFrom(Location from) {
return checkAndSet(this.from, from, location -> this.from = location);
}
public CRay setTo(Location to) {
return checkAndSet(this.to, to, location -> this.to = location);
}
public CRay setWidth(float width) {
return checkAndSet(this.width, width, location -> this.width = width);
}
public CRay setBlock(BlockData blockData) {
if (this.blockData.equals(blockData)) return this;
if (blockData == null) {
this.blockData = RBlockDisplay.DEFAULT_BLOCK;
} else {
this.blockData = blockData;
}
if (display != null) {
display.setBlock(blockData);
}
return this;
}
@Override
public void hide(boolean hide) {
if (hide == this.hide) return;
this.hide = hide;
if (hide) {
if (display != null) display.hide(true);
} else {
tick();
}
}
@Override
void tick() {
if (from == null || to == null) return;
if (hide) return;
updateDisplay();
}
private RBlockDisplay display;
private void updateDisplay() {
if (display == null) {
display = new RBlockDisplay(server, new Location(null, 0, 0, 0));
display.setBrightness(new Display.Brightness(15, 15));
display.setViewRange(560);
display.setBlock(blockData);
entities.add(display);
} else {
display.hide(false);
}
Vector3f pointA = new Vector3f((float) from.getX(), (float) from.getY(), (float) from.getZ());
Vector3f pointB = new Vector3f((float) to.getX(), (float) to.getY(), (float) to.getZ());
Vector3f direction = new Vector3f(pointB).sub(pointA);
float length = direction.length();
display.move(from);
Vector3f normalizedDir = new Vector3f(direction).normalize();
final Vector3f defaultAxis = new Vector3f(1, 0, 0);
Quaternionf rotation = new Quaternionf().rotationTo(defaultAxis, normalizedDir);
Vector3f scale = new Vector3f(length, width, width);
Transformation transformation = new Transformation(new Vector3f(0, 0, 0), rotation, scale, new Quaternionf());
display.setTransform(transformation);
}
}

View File

@@ -189,6 +189,7 @@ public class REntityServer implements Listener {
private void removeEntityFromChunk(REntity entity) {
long id = entityToId(entity);
HashSet<REntity> entitiesInChunk = entities.get(id);
if (entitiesInChunk == null) return;
entitiesInChunk.remove(entity);
if(entitiesInChunk.isEmpty())
entities.remove(id);

View File

@@ -35,6 +35,7 @@ import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.UUID;
import java.util.function.Consumer;
@@ -70,7 +71,7 @@ public class RPlayer extends REntity {
private final String name;
public RPlayer(REntityServer server, UUID uuid, String name, Location location) {
super(server, EntityType.PLAYER, UUID.randomUUID(), location,0);
super(server, EntityType.PLAYER, UUID.nameUUIDFromBytes(uuid.toString().getBytes(StandardCharsets.UTF_8)), location,0);
this.actualUUID = uuid;
this.name = name;
server.addEntity(this);

View File

@@ -47,7 +47,7 @@ public class RTextDisplay extends RDisplay {
private boolean seeThrough;
private boolean defaultBackground;
private Integer backgroundColor;
private TextDisplay.TextAlignment alignment;
@@ -58,7 +58,7 @@ public class RTextDisplay extends RDisplay {
this.textOpacity = (byte) -1;
this.shadowed = false;
this.seeThrough = false;
this.defaultBackground = false;
this.backgroundColor = null;
this.alignment = TextDisplay.TextAlignment.CENTER;
server.addEntity(this);
}
@@ -116,8 +116,22 @@ public class RTextDisplay extends RDisplay {
sendPacket(updatePacketSink, this::getTextStatus);
}
public void setBackgroundColor(int color) {
this.backgroundColor = color;
sendPacket(updatePacketSink, this::getTextStatus, this::getBackgroundColor);
}
private static final Object backgroundColorWatcher = BountifulWrapper.impl.getDataWatcherObject(Core.getVersion() >= 21 ? 25 : 24, Integer.class);
private void getBackgroundColor(boolean ignoreDefault, BiConsumer<Object, Object> packetSink) {
if (ignoreDefault || backgroundColor != null) {
packetSink.accept(backgroundColorWatcher, backgroundColor);
}
}
public void setDefaultBackground(boolean defaultBackground) {
this.defaultBackground = defaultBackground;
if (defaultBackground) {
this.backgroundColor = null;
}
sendPacket(updatePacketSink, this::getTextStatus);
}
@@ -136,7 +150,7 @@ public class RTextDisplay extends RDisplay {
if (seeThrough) {
status |= 0x02;
}
if (defaultBackground) {
if (backgroundColor == null) {
status |= 0x04;
}
if (alignment == TextDisplay.TextAlignment.CENTER) {

View File

@@ -86,6 +86,10 @@ public class SWInventory implements Listener {
}
}
public boolean hasItem(int pos) {
return inventory.getItem(pos) != null;
}
public void setItem(int pos, SWItem item) {
setItem(pos, item.getItemStack(), item.getCallback());
}

View File

@@ -220,4 +220,9 @@ public class SWItem {
itemStack.setItemMeta(itemMeta);
return this;
}
public SWItem setAmount(int count) {
itemStack.setAmount(count);
return this;
}
}

View File

@@ -59,6 +59,11 @@ public class CoreNetworkHandler extends PacketHandler {
InventoryHandler.handleInventoryPacket(packet);
}
@Handler
public void handleAnvilPacket(AnvilInventoryPacket packet) {
InventoryHandler.handleAnvilInventoryPacket(packet);
}
@Handler
public void handlePingPacket(PingPacket packet) {
UUID uuid = SteamwarUser.get(packet.getId()).getUUID();

View File

@@ -20,11 +20,15 @@
package de.steamwar.network.handlers;
import com.google.gson.JsonParser;
import de.steamwar.inventory.SWAnvilInv;
import de.steamwar.inventory.SWInventory;
import de.steamwar.inventory.SWItem;
import de.steamwar.network.NetworkSender;
import de.steamwar.network.packets.NetworkPacket;
import de.steamwar.network.packets.PacketHandler;
import de.steamwar.network.packets.client.AnvilAnswerPacket;
import de.steamwar.network.packets.client.InventoryCallbackPacket;
import de.steamwar.network.packets.server.AnvilInventoryPacket;
import de.steamwar.network.packets.server.InventoryPacket;
import de.steamwar.sql.SteamwarUser;
import org.bukkit.Bukkit;
@@ -53,4 +57,20 @@ public class InventoryHandler extends PacketHandler {
});
inventory.open();
}
@Handler
public static void handleAnvilInventoryPacket(AnvilInventoryPacket packet) {
Player player = Bukkit.getPlayer(SteamwarUser.get(packet.getPlayerId()).getUUID());
SWAnvilInv inv = new SWAnvilInv(player, packet.getTitle(), packet.getDefaultText());
if (packet.getMaterial() != null && !packet.getMaterial().isEmpty()) {
inv.setItem(SWItem.getMaterial(packet.getMaterial()));
}
inv.setCallback(s -> NetworkSender.send(AnvilAnswerPacket.builder().playerId(packet.getPlayerId()).action(AnvilAnswerPacket.Action.ANSWER).text(s).build()));
inv.addLeftCallback(() -> NetworkSender.send(AnvilAnswerPacket.builder().playerId(packet.getPlayerId()).action(AnvilAnswerPacket.Action.LEFT_CLICK).build()));
inv.addCloseCallback(() -> NetworkSender.send(AnvilAnswerPacket.builder().playerId(packet.getPlayerId()).action(AnvilAnswerPacket.Action.CLOSE).build()));
inv.open();
}
}

View File

@@ -53,6 +53,7 @@ public interface ChunkHider {
private boolean paletted;
private int bitsPerBlock;
private int blockCount;
private int air;
private int target;
private Set<Integer> obfuscate;
@@ -86,7 +87,8 @@ public interface ChunkHider {
}
public void copyBlockCount() {
out.writeShort(in.readShort());
this.blockCount = in.readShort();
out.writeShort(blockCount);
}
public void copyBitsPerBlock() {
@@ -140,6 +142,16 @@ public interface ChunkHider {
out.writeBytes(in, dataArrayLength*8);
}
public void skipNewDataArray(int entries) {
if (bitsPerBlock == 0) {
return;
}
char valuesPerLong = (char)(64 / bitsPerBlock);
int i1 = (entries + valuesPerLong - 1) / valuesPerLong;
out.writeBytes(in, i1 * Long.BYTES);
}
public long[] readDataArray() {
long[] array = new long[copyVarInt()];
for(int i = 0; i < array.length; i++)
@@ -148,6 +160,20 @@ public interface ChunkHider {
return array;
}
public long[] readNewDataArray(int entries) {
if (bitsPerBlock == 0) {
return new long[entries];
}
char valuesPerLong = (char) (64 / bitsPerBlock);
int i1 = (entries + valuesPerLong - 1) / valuesPerLong;
long[] array = new long[i1];
for(int i = 0; i < i1; i++)
array[i] = in.readLong();
return array;
}
public void writeDataArray(long[] array) {
for(long l : array)
out.writeLong(l);

View File

@@ -58,6 +58,16 @@ public class ProtocolUtils {
};
}
public static <T> UnaryOperator<T> shallowTypedCloneGenerator(Class<T> clazz) {
BiConsumer<Object, Object> filler = shallowFill(clazz);
return source -> {
Object clone = Reflection.newInstance(clazz);
filler.accept(source, clone);
return (T) clone;
};
}
private static BiConsumer<Object, Object> shallowFill(Class<?> clazz) {
if(clazz == null)
return (source, clone) -> {};

View File

@@ -1,7 +1,7 @@
name: SpigotCore
version: "2.0"
author: Lixfel
api-version: "1.13"
api-version: "1.21"
load: STARTUP
softdepend:
- WorldEdit

View File

@@ -19,12 +19,14 @@
package de.steamwar.teamserver;
import de.steamwar.core.Core;
import de.steamwar.core.WorldEditRendererCUIEditor;
import de.steamwar.message.Message;
import de.steamwar.teamserver.command.*;
import de.steamwar.teamserver.listener.AxiomHandshakeListener;
import de.steamwar.teamserver.listener.FreezeListener;
import de.steamwar.teamserver.listener.PlayerChange;
import de.steamwar.teamserver.listener.SelectAdjacent;
import org.bukkit.Bukkit;
import org.bukkit.GameRule;
import org.bukkit.plugin.java.JavaPlugin;
@@ -60,6 +62,10 @@ public final class Builder extends JavaPlugin {
Bukkit.getWorlds().get(0).setGameRule(GameRule.REDUCED_DEBUG_INFO, false);
new WorldEditRendererCUIEditor();
if (Core.getVersion() >= 20) {
Bukkit.getPluginManager().registerEvents(new SelectAdjacent(), this);
}
}
@Override

View File

@@ -0,0 +1,177 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.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.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;
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);
}
}
}
}
}

View File

@@ -220,7 +220,8 @@ public class Subserver {
try {
if (checkpoint) {
start(process.getErrorStream(), line -> line.contains("Restore finished successfully."));
Thread.sleep(300);
start(process.getInputStream(), line -> line.contains("Checkpoint restored"));
Thread.sleep(100);
} else {
start(process.getInputStream(), line -> {
if (line.contains("Loading libraries, please wait"))

View File

@@ -46,7 +46,9 @@ public class TypeUtils {
Player player = sender.getPlayer();
if (player != null && s.isEmpty()) {
ProtocolVersion version = player.getProtocolVersion();
if (version.greaterThan(ProtocolVersion.MINECRAFT_1_19_4)) {
if (version.greaterThan(ProtocolVersion.MINECRAFT_1_20_5)) {
return ServerVersion.PAPER_21;
} else if (version.greaterThan(ProtocolVersion.MINECRAFT_1_19_4)) {
return ServerVersion.PAPER_20;
} else if (version.greaterThan(ProtocolVersion.MINECRAFT_1_15_2)) {
return ServerVersion.PAPER_19;

View File

@@ -61,7 +61,7 @@ public class ServerStarter {
private String worldDir = null;
private Node node = null;
private ServerVersion version = ServerVersion.PAPER_20;
private ServerVersion version = ServerVersion.PAPER_21;
private Portrange portrange = BAU_PORTS;
private Function<Integer, String> serverNameProvider = port -> node.getName() + port;
private BooleanSupplier startCondition = () -> true;

View File

@@ -224,7 +224,7 @@ public class VelocityCore implements ReloadablePlugin {
for(PacketHandler handler : new PacketHandler[] {
new EloPlayerHandler(), new EloSchemHandler(), new ExecuteCommandHandler(), new FightInfoHandler(),
new ImALobbyHandler(), new InventoryCallbackHandler(), new PrepareSchemHandler(), new PlayerSkinHandler()
new ImALobbyHandler(), new InventoryCallbackHandler(), new PrepareSchemHandler(), new PlayerSkinHandler(), new AnvilAnswerHandler()
})
handler.register();

View File

@@ -73,6 +73,14 @@ public class DevCommand extends SWCommand {
sender.getPlayer().createConnectionRequest(server).fireAndForget();
}
@Register(value = "reloadmodes")
public void reloadModes(Chatter sender) {
if(!sender.user().hasPerm(UserPerm.ADMINISTRATION))
return;
ArenaMode.init();
}
@Register
public void selectedCommand(@Validator PlayerChatter sender, @Mapper("dev") String name) {
updateDevServers();
@@ -85,14 +93,6 @@ public class DevCommand extends SWCommand {
sender.getPlayer().createConnectionRequest(server).fireAndForget();
}
@Register(value = "reloadmodes")
public void reloadModes(Chatter sender) {
if(!sender.user().hasPerm(UserPerm.ADMINISTRATION))
return;
ArenaMode.init();
}
@ClassValidator(value = PlayerChatter.class, local = true)
public TypeValidator<PlayerChatter> punishmentGuardChecker() {
return (sender, value, messageSender) -> {

View File

@@ -41,7 +41,6 @@ import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.emoji.Emoji;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
import net.dv8tion.jda.api.interactions.commands.Command;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
@@ -49,13 +48,14 @@ import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import net.dv8tion.jda.api.requests.GatewayIntent;
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
import net.dv8tion.jda.api.utils.MemberCachePolicy;
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.*;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
@@ -67,9 +67,10 @@ public class DiscordBot {
@Getter
private static final Map<String, SWCommand> commands = new HashMap<>();
private final OptionData commandArgument = new OptionData(OptionType.STRING, ARGUMENT_NAME, "Command arguments", false);
public static void withBot(Consumer<DiscordBot> consumer) {
if(instance != null)
if (instance != null)
consumer.accept(instance);
}
@@ -130,6 +131,7 @@ public class DiscordBot {
reply.system("DC_ROLE_ADDED", role.getAsMention());
}
}));
new StaticMessageChannel(config.channel("rules"), () -> new MessageCreateBuilder()
.setEmbeds(new EmbedBuilder()
.setDescription(String.join("\n", config.getRules()))
@@ -141,9 +143,10 @@ public class DiscordBot {
ActionRow.of(Button.link("https://steamwar.de", "Website"), Button.link("https://steamwar.de/youtube", "YouTube")),
ActionRow.of(Button.primary("auth", Emoji.fromUnicode("U+2705")).withLabel("Minecraft verknüpfen"))
), event -> {
if(event.getComponentId().equals("auth"))
if (event.getComponentId().equals("auth"))
event.reply("Gebe innerhalb der nächsten 10 Minuten ``/verify " + AuthManager.createDiscordAuthToken(event.getUser()) + "`` auf dem Minecraft Server ein").setEphemeral(true).queue();
});
List<ActionRow> actionRows = new ArrayList<>();
List<Button> list = new ArrayList<>();
for (DiscordTicketType type : DiscordTicketType.values()) {
@@ -163,6 +166,7 @@ public class DiscordBot {
.setColor(Color.RED)
.build())
.setComponents(actionRows), DiscordTicketHandler::openTicket);
eventChannel = new StaticMessageChannel(config.channel("events"), EventChannel::get);
checklistChannel = new ChecklistChannel(config.channel("checklist"));
config.getCouncilThread().forEach((roleId, threadId) -> new CouncilChannel(DiscordBot.getGuild().getRoleById(roleId), DiscordBot.getGuild().getThreadChannelById(threadId)));
@@ -193,45 +197,32 @@ public class DiscordBot {
VelocityCore.schedule(CouncilChannel::updateAll).repeat(1, TimeUnit.HOURS).schedule();
VacationCommand vacationCommand = new VacationCommand();
jda.addEventListener(
new DiscordTicketHandler(),
new DiscordTeamEvent(),
new ChannelListener(),
new DiscordSchemUpload()
new DiscordSchemUpload(),
vacationCommand
);
commandSetup(jda.retrieveCommands().complete(), jda.updateCommands());
}
private final OptionData commandArgument = new OptionData(OptionType.STRING, ARGUMENT_NAME, "Command arguments", false);
private void commandSetup(List<Command> existing, CommandListUpdateAction updateCommands) {
Set<String> correctCommands = new HashSet<>();
for(Command command : existing) {
if(!commands.containsKey(command.getName())) {
command.delete().complete();
continue;
}
List<Command.Option> options = command.getOptions();
if(options.size() != 1 || options.get(0).getType() != OptionType.STRING)
command.editCommand().clearOptions().addOptions(commandArgument).complete();
correctCommands.add(command.getName());
}
updateCommands
.addCommands(commands
.keySet().stream()
.filter(command -> !correctCommands.contains(command))
jda.updateCommands()
.addCommands(commands.keySet()
.stream()
.filter(command -> command.matches("^[\\w-]+$"))
.map(command -> Commands.slash(command, command).addOptions(commandArgument))
.toArray(CommandData[]::new))
.queue();
jda.getGuildById(1241489896909180998L)
.upsertCommand(vacationCommand.COMMAND)
.queue();
}
private boolean activityToggle = false;
private void activity() {
if(activityToggle) {
if (activityToggle) {
Event event = Event.get();
jda.getPresence().setActivity(event != null ? Activity.competing("dem Event " + event.getEventName()) : Activity.playing("auf SteamWar.de"));
} else {

View File

@@ -0,0 +1,189 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2020 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.velocitycore.discord;
import it.unimi.dsi.fastutil.Pair;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.ScheduledEvent;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.commands.Command;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
import net.dv8tion.jda.internal.interactions.CommandDataImpl;
import org.jetbrains.annotations.NotNull;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
public class VacationCommand extends ListenerAdapter {
private final Guild guild = Objects.requireNonNull(DiscordBot.getInstance().getJda().getGuildById(1241489896909180998L));
public final CommandDataImpl COMMAND = new CommandDataImpl("vacation", "Verwalte deinen Urlaub");
public VacationCommand() {
COMMAND.addSubcommands(new SubcommandData("create", "Erstelle deinen Urlaub")
.addOptions(new OptionData(OptionType.STRING, "from", "Datum (TT.MM.JJJJ)", true),
new OptionData(OptionType.STRING, "to", "Datum (TT.MM.JJJJ)", true)));
COMMAND.addSubcommands(new SubcommandData("delete", "Lösche deinen Urlaub")
.addOptions(new OptionData(OptionType.STRING, "vacation", "Dein Urlaub", true, true)));
}
@Override
public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) {
if (!event.getName().equals("vacation")) return;
switch (event.getSubcommandName()) {
case "create":
createVacation(event);
break;
case "delete":
deleteVacation(event);
break;
default:
break;
}
}
public static final DateTimeFormatter PARSER = DateTimeFormatter.ofPattern("dd.MM.uuuu");
private void createVacation(SlashCommandInteractionEvent event) {
InteractionHook interactionHook = event.deferReply(true).complete();
String from = event.getOption("from").getAsString();
String to = event.getOption("to").getAsString();
LocalDateTime fromDate;
try {
fromDate = LocalDate.parse(from, PARSER).atStartOfDay();
} catch (DateTimeParseException e) {
interactionHook.editOriginal("Das Datumsformat ist falsch! Bitte verwenden Sie TT.MM.JJJJ für den ersten Tag deines Urlaubs").queue();
return;
}
if (fromDate == null) {
interactionHook.editOriginal("Das Datumsformat ist falsch! Bitte verwenden Sie TT.MM.JJJJ für den ersten Tag deines Urlaubs").queue();
return;
}
if (!fromDate.isAfter(LocalDateTime.now())) {
interactionHook.editOriginal("Bitte gib ein Datum in der Zukunft an!").queue();
return;
}
LocalDateTime toDate;
try {
toDate = LocalDate.parse(to, PARSER).atTime(23, 59, 59);
} catch (DateTimeParseException e) {
interactionHook.editOriginal("Das Datumsformat ist falsch! Bitte verwenden Sie TT.MM.JJJJ für den letzten Tag deines Urlaubs").queue();
return;
}
if (toDate == null) {
interactionHook.editOriginal("Das Datumsformat ist falsch! Bitte verwenden Sie TT.MM.JJJJ für den letzten Tag deines Urlaubs").queue();
return;
}
if (!toDate.isAfter(LocalDateTime.now())) {
interactionHook.editOriginal("Bitte gib ein Datum in der Zukunft an!").queue();
return;
}
if (!toDate.isAfter(fromDate)) {
interactionHook.editOriginal("Bitte gib ein Datum nach dem ersten Urlaubstag an!").queue();
return;
}
guild.createScheduledEvent(
"Urlaub " + event.getMember().getEffectiveName(),
event.getMember().getId(),
OffsetDateTime.of(fromDate, ZoneId.of("Europe/Berlin").getRules().getOffset(fromDate)),
OffsetDateTime.of(toDate, ZoneId.of("Europe/Berlin").getRules().getOffset(toDate))
).onSuccess(scheduledEvent -> {
interactionHook.editOriginal("Urlaub erstellt!").queue();
})
.onErrorMap(throwable -> {
interactionHook.editOriginal("Urlaub konnte nicht erstellt werden!").queue();
return null;
})
.queue();
}
private void deleteVacation(SlashCommandInteractionEvent event) {
InteractionHook interactionHook = event.deferReply(true).complete();
String eventId = event.getOption("vacation").getAsString();
ScheduledEvent scheduledEvent = guild.getScheduledEventById(eventId);
if (scheduledEvent == null) {
interactionHook.editOriginal("Konnte den Urlaub nicht finden!").queue();
return;
}
scheduledEvent.delete()
.onSuccess(unused -> {
interactionHook.editOriginal("Urlaub gelöscht!").queue();
})
.onErrorMap(throwable -> {
interactionHook.editOriginal("Urlaub konnte nicht gelöscht werden!").queue();
return null;
})
.queue();
}
@Override
public void onCommandAutoCompleteInteraction(@NotNull CommandAutoCompleteInteractionEvent event) {
if (!event.getName().equals("vacation")) return;
switch (event.getFocusedOption().getName()) {
case "vacation":
listVacations(event);
break;
default:
break;
}
}
private void listVacations(CommandAutoCompleteInteractionEvent event) {
String vacation = event.getOption("vacation").getAsString();
List<Command.Choice> choices = guild.getScheduledEvents()
.stream()
.filter(scheduledEvent -> scheduledEvent.getLocation().equals(event.getMember().getId()))
.map(scheduledEvent -> {
StringBuilder st = new StringBuilder();
st.append(String.format("%02d", scheduledEvent.getStartTime().getDayOfMonth())).append(".");
st.append(String.format("%02d", scheduledEvent.getStartTime().getMonthValue())).append(".");
st.append(scheduledEvent.getStartTime().getYear());
st.append(" - ");
st.append(String.format("%02d", scheduledEvent.getEndTime().getDayOfMonth())).append(".");
st.append(String.format("%02d", scheduledEvent.getEndTime().getMonthValue())).append(".");
st.append(scheduledEvent.getEndTime().getYear());
return Pair.of(scheduledEvent, st.toString());
})
.filter(pair -> pair.right().startsWith(vacation))
.limit(25)
.map(pair -> {
return new Command.Choice(pair.right(), pair.left().getId());
})
.collect(Collectors.toList());
event.replyChoices(choices)
.queue();
}
}

View File

@@ -70,6 +70,7 @@ public class ChannelListener extends ListenerAdapter {
@Override
public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) {
if (event.getGuild().getIdLong() == 1241489896909180998L) return;
InteractionReply.reply(event, sender -> {
if(sender.user().getDiscordId() == null)
return;

View File

@@ -0,0 +1,69 @@
/*
* 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.velocitycore.inventory;
import com.velocitypowered.api.proxy.Player;
import de.steamwar.network.packets.client.AnvilAnswerPacket;
import de.steamwar.network.packets.server.AnvilInventoryPacket;
import de.steamwar.sql.SteamwarUser;
import de.steamwar.velocitycore.network.NetworkSender;
import lombok.AllArgsConstructor;
import lombok.Builder;
import java.util.HashMap;
import java.util.function.Consumer;
@AllArgsConstructor
@Builder(toBuilder = true)
public class SWAnvilInv {
private static final HashMap<Integer, SWAnvilInv> openInv = new HashMap<>();
public static void handleClick(AnvilAnswerPacket packet) {
try {
switch (packet.getAction()) {
case LEFT_CLICK:
openInv.get(packet.getPlayerId()).leftClickCallback.run();
break;
case CLOSE:
openInv.remove(packet.getPlayerId()).closeCallback.run();
break;
case ANSWER:
openInv.get(packet.getPlayerId()).callback.accept(packet.getText());
break;
}
} catch (NullPointerException ignored) { }
}
private final Player player;
private final String title;
private final String defaultText;
private final String material;
private final Consumer<String> callback;
private final Runnable closeCallback;
private final Runnable leftClickCallback;
public void open() {
SteamwarUser user = SteamwarUser.get(player.getUniqueId());
openInv.put(user.getId(), this);
NetworkSender.send(player, new AnvilInventoryPacket(user.getId(), title, defaultText, material));
}
}

View File

@@ -57,6 +57,7 @@ public class Hostname extends BasicListener {
knownHostnames.add("78.31.71.136");
knownHostnames.add("memewar.de"); // Chaoscaot
knownHostnames.add("dampfkrieg.de"); // Chaoscaot
knownHostnames.add("steamshrimp.de"); // Chaoscaot
knownHostnames.add("127.0.0.1"); // Geyser
knownHostnames.add("@mat:matdoes.dev "); //https://github.com/mat-1/matscan

View File

@@ -0,0 +1,32 @@
/*
* 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.velocitycore.network.handlers;
import de.steamwar.network.packets.PacketHandler;
import de.steamwar.network.packets.client.AnvilAnswerPacket;
import de.steamwar.velocitycore.inventory.SWAnvilInv;
public class AnvilAnswerHandler extends PacketHandler {
@Handler
public void handle(AnvilAnswerPacket packet) {
SWAnvilInv.handleClick(packet);
}
}

View File

@@ -46,9 +46,15 @@ class DevServer extends DefaultTask {
@Optional
Map<String, String> dParams = new HashMap<>()
@Input
@Optional
String checkpointFolder = null
DevServer() {
super()
doFirst {
if (checkpointFolder != null) dParams.put("checkpoint", checkpointFolder)
List<Project> projects = []
projects.add(project)
while (projects.first.parent != null) {

View File

@@ -126,7 +126,7 @@ dependencyResolutionManagement {
library("nms18", "de.steamwar:spigot:1.18")
library("nms19", "de.steamwar:spigot:1.19")
library("nms20", "de.steamwar:spigot:1.20")
library("nms21", "de.steamwar:spigot:1.21.5")
library("nms21", "de.steamwar:spigot:1.21.6")
library("axiom", "de.steamwar:axiompaper:RELEASE")
library("worldedit12", "de.steamwar:worldedit:1.12")