Compare commits

..
41 changed files with 1828 additions and 700 deletions
@@ -1,37 +0,0 @@
package de.steamwar.bausystem.features.printerTool;
import de.steamwar.bausystem.utils.ItemUtils;
import de.steamwar.inventory.SWItem;
import de.steamwar.linkage.Linked;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
@Linked
public class PrinterToolItem implements Listener {
public static ItemStack getItem(Player p) {
ItemStack itemStack = new SWItem(Material.WOODEN_PICKAXE, "Printer Tool").getItemStack();
ItemMeta itemMeta = itemStack.getItemMeta();
itemMeta.setCustomModelData(1);
itemStack.setItemMeta(itemMeta);
ItemUtils.setItem(itemStack, "printer_tool");
return itemStack;
}
public static boolean isItem(ItemStack itemStack) {
return itemStack != null && ItemUtils.isItem(itemStack, "printer_tool");
}
@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
ItemStack item = event.getItem();
if(PrinterToolItem.isItem(item)) {
}
}
}
@@ -226,7 +226,7 @@ public class FreezeListener implements Listener, ScoreboardElement {
} }
} }
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) @EventHandler(priority = EventPriority.MONITOR)
public void onBlockBreak(BlockBreakEvent e) { public void onBlockBreak(BlockBreakEvent e) {
if (Core.getVersion() < 19) return; if (Core.getVersion() < 19) return;
if (e.getPlayer().getInventory().getItemInMainHand().getType() == Material.DEBUG_STICK) return; if (e.getPlayer().getInventory().getItemInMainHand().getType() == Material.DEBUG_STICK) return;
@@ -25,7 +25,6 @@ import com.sk89q.worldedit.world.block.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypes; import com.sk89q.worldedit.world.block.BlockTypes;
import de.steamwar.bausystem.utils.WorldEditUtils; import de.steamwar.bausystem.utils.WorldEditUtils;
import de.steamwar.core.Core;
import lombok.Getter; import lombok.Getter;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import org.bukkit.Location; import org.bukkit.Location;
@@ -57,7 +56,7 @@ public class Panzern {
private BaseBlock blockType; private BaseBlock blockType;
private BaseBlock slabType; private BaseBlock slabType;
private static final BaseBlock jukeboxType = (Core.getVersion() > 19 ? BlockTypes.get("lodestone"): BlockTypes.get("jukebox")).getDefaultState().toBaseBlock(); private static final BaseBlock jukeboxType = BlockTypes.JUKEBOX.getDefaultState().toBaseBlock();
private static final BaseBlock cobwebType = BlockTypes.COBWEB.getDefaultState().toBaseBlock(); private static final BaseBlock cobwebType = BlockTypes.COBWEB.getDefaultState().toBaseBlock();
private static final BaseBlock airType = BlockTypes.AIR.getDefaultState().toBaseBlock(); private static final BaseBlock airType = BlockTypes.AIR.getDefaultState().toBaseBlock();
@@ -179,12 +179,7 @@ public class SpectatorListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) { public void onPlayerCommandPreprocess(PlayerCommandPreprocessEvent event) {
if (event.getMessage().startsWith("/schem save") || if (event.getMessage().startsWith("/schem save") || event.getMessage().startsWith("//schem save") || event.getMessage().startsWith("/schematic save") || event.getMessage().startsWith("//schematic save")) {
event.getMessage().startsWith("//schem save") ||
event.getMessage().startsWith("/schematic save") ||
event.getMessage().startsWith("//schematic save") ||
event.getMessage().startsWith("/download") ||
event.getMessage().startsWith("//download")) {
if (!Permission.SUPERVISOR.hasPermission(event.getPlayer())) { if (!Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
event.setCancelled(true); event.setCancelled(true);
event.setMessage("/"); event.setMessage("/");
@@ -1,204 +0,0 @@
package de.steamwar.bausystem.utils.cursor;
import de.steamwar.bausystem.utils.RayTraceUtils;
import de.steamwar.entity.REntity;
import de.steamwar.entity.REntityServer;
import de.steamwar.entity.RFallingBlockEntity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.block.Action;
import org.bukkit.util.Vector;
import yapion.hierarchy.types.YAPIONType;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
@Getter
public class RCursor {
private final World WORLD = Bukkit.getWorlds().get(0);
private final REntityServer targetServer;
private final REntityServer cursorServer;
private final Player owner;
private final Material highlightMaterial;
private final ClickHandler onClickHandler;
private RFallingBlockEntity cursorEntity;
private Location cursorLocation;
private boolean isHittingEntity = false;
@Setter
private Material cursorMaterial;
@Setter
private CursorMode cursorMode;
private boolean visible = true;
public RCursor(REntityServer targetServer, Player owner, Material highlightMaterial, Material cursorMaterial, CursorMode cursorMode, ClickHandler onClickHandler) {
this.targetServer = targetServer;
this.owner = owner;
this.highlightMaterial = highlightMaterial;
this.cursorMaterial = cursorMaterial;
this.cursorMode = cursorMode;
this.onClickHandler = onClickHandler;
cursorServer = new REntityServer();
cursorServer.addPlayer(owner);
RCursorManager.getInstance().registerCursor(this);
}
public void render() {
if (!visible) return;
RayTraceUtils.RRayTraceResult rayTraceResult = RayTraceUtils.traceREntity(owner, owner.getLocation(), targetServer.getEntities());
if (rayTraceResult == null) {
if(cursorEntity != null) cursorEntity.die();
cursorEntity = null;
return;
}
REntity hitEntity = rayTraceResult.getHitEntity() == cursorEntity ? null : rayTraceResult.getHitEntity();
Material activeCursorMaterial = hitEntity == null ? cursorMaterial : highlightMaterial;
Location activeCursorLocation = hitEntity == null ? cursorMode.positionTransform.apply(owner, rayTraceResult).toLocation(WORLD)
: new Vector(hitEntity.getX(), hitEntity.getY(), hitEntity.getZ()).toLocation(WORLD);
cursorLocation = activeCursorLocation;
isHittingEntity = hitEntity != null;
if (cursorEntity == null) {
cursorEntity = new RFallingBlockEntity(cursorServer, activeCursorLocation, activeCursorMaterial);
cursorEntity.setNoGravity(true);
} if (cursorEntity.getMaterial() == activeCursorMaterial) {
cursorEntity.move(activeCursorLocation);
} else {
cursorEntity.die();
cursorEntity = new RFallingBlockEntity(cursorServer, activeCursorLocation, activeCursorMaterial);
cursorEntity.setNoGravity(true);
if(activeCursorMaterial == highlightMaterial) {
cursorEntity.setGlowing(true);
}
}
}
public void hide() {
visible = false;
if (cursorEntity != null) {
cursorEntity.die();
cursorEntity = null;
}
}
public void show() {
visible = true;
}
void handleClick(Action action) {
if(!visible || cursorLocation == null) return;
onClickHandler.handle(cursorLocation, isHittingEntity, action);
}
public void close() {
cursorServer.close();
RCursorManager.getInstance().unregisterCursor(this);
}
@AllArgsConstructor
public enum CursorMode {
FREE((player, rayTraceResult) -> {
Vector pos = rayTraceResult.getHitPosition();
BlockFace face = rayTraceResult.getHitBlockFace();
if (face != null) {
switch (face) {
case DOWN:
pos.setY(pos.getY() - 0.98);
break;
case EAST:
pos.setX(pos.getX() + 0.49);
break;
case WEST:
pos.setX(pos.getX() - 0.49);
break;
case NORTH:
pos.setZ(pos.getZ() - 0.49);
break;
case SOUTH:
pos.setZ(pos.getZ() + 0.49);
break;
default:
break;
}
if (face.getModY() == 0 && player.isSneaking()) {
pos.setY(pos.getY() - 0.49);
}
}
if (!player.isSneaking()) {
pos.setX(pos.getBlockX() + 0.5);
if (face == null || face.getModY() == 0)
pos.setY(pos.getBlockY() + 0.0);
pos.setZ(pos.getBlockZ() + 0.5);
}
return pos;
}),
BLOCK_ALIGNED((player, rayTraceResult) -> {
Vector pos = rayTraceResult.getHitPosition();
BlockFace face = rayTraceResult.getHitBlockFace();
if (face != null) {
switch (face) {
case DOWN:
pos.setY(pos.getY() - 0.98);
break;
case EAST:
pos.setX(pos.getX() + 0.49);
break;
case WEST:
pos.setX(pos.getX() - 0.49);
break;
case NORTH:
pos.setZ(pos.getZ() - 0.49);
break;
case SOUTH:
pos.setZ(pos.getZ() + 0.49);
break;
default:
break;
}
}
pos.setX(pos.getBlockX() + 0.5);
if (pos.getY() - pos.getBlockY() != 0 && face == BlockFace.UP) {
pos.setY(pos.getBlockY() + 1.0);
} else {
pos.setY(pos.getBlockY());
}
pos.setZ(pos.getBlockZ() + 0.5);
return pos;
});
private final BiFunction<Player, RayTraceUtils.RRayTraceResult, Vector> positionTransform;
}
public interface ClickHandler{
public void handle(Location cursorLocation, boolean didHitEntity, Action action);
}
}
@@ -1,112 +0,0 @@
package de.steamwar.bausystem.utils.cursor;
import com.comphenix.tinyprotocol.TinyProtocol;
import de.steamwar.Reflection;
import de.steamwar.bausystem.BauSystem;
import de.steamwar.linkage.Linked;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
@Linked
public class RCursorManager implements Listener {
@Getter
private static RCursorManager instance;
private final Set<Player> calculationActive = new HashSet<>();
private final Map<Player, RCursor> activeCursors = new HashMap<>();
public RCursorManager() {
if (instance == null) {
instance = this;
}
BiFunction<Player, Object, Object> function = (player, object) -> {
updateCursor(player);
return object;
};
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
Set<Player> playersWithActiveCursor = activeCursors.keySet();
playersWithActiveCursor.forEach(this::updateCursor);
}, 0);
Class<?> positionPacketClass = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Pos");
Class<?> lookPacketClass = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$Rot");
Class<?> positionLookPacketClass = Reflection.getClass("net.minecraft.network.protocol.game.ServerboundMovePlayerPacket$PosRot");
TinyProtocol.instance.addFilter(positionPacketClass, function);
TinyProtocol.instance.addFilter(lookPacketClass, function);
TinyProtocol.instance.addFilter(positionLookPacketClass, function);
}
void registerCursor(RCursor cursor) {
closeCursorOf(cursor.getOwner());
activeCursors.put(cursor.getOwner(), cursor);
}
void unregisterCursor(RCursor cursor) {
activeCursors.remove(cursor.getOwner());
}
private void closeCursorOf(Player player) {
RCursor currentPlayerCursor = activeCursors.get(player);
if(currentPlayerCursor != null) {
currentPlayerCursor.close();
};
}
private void updateCursor(Player player) {
if (!activeCursors.containsKey(player)) {
return;
}
synchronized (calculationActive) {
if (calculationActive.contains(player)) {
return;
} else {
calculationActive.add(player);
}
}
RCursor cursor = activeCursors.get(player);
if (cursor != null) {
cursor.render();
}
synchronized (calculationActive) {
calculationActive.remove(player);
}
}
@EventHandler
private void handlePlayerInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();
RCursor cursorOfPlayer = activeCursors.get(player);
cursorOfPlayer.handleClick(event.getAction());
}
@EventHandler
private void handlePlayerQuit(PlayerQuitEvent event) {
closeCursorOf(event.getPlayer());
}
}
-16
View File
@@ -34,19 +34,3 @@ dependencies {
implementation(project(":BauSystem:BauSystem_20")) implementation(project(":BauSystem:BauSystem_20"))
implementation(project(":BauSystem:BauSystem_21")) implementation(project(":BauSystem:BauSystem_21"))
} }
tasks.register<DevServer>("DevBau20") {
group = "run"
description = "Run a 1.20 Dev Bau"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":BauSystem:shadowJar")
template = "Bau20"
}
tasks.register<DevServer>("DevBau21") {
group = "run"
description = "Run a 1.21 Dev Bau"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":BauSystem:shadowJar")
template = "Bau21"
}
@@ -34,7 +34,6 @@ import org.bukkit.command.CommandSender;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.logging.Level;
public class TechhiderbugCommand implements CommandExecutor { public class TechhiderbugCommand implements CommandExecutor {
@@ -46,33 +45,25 @@ public class TechhiderbugCommand implements CommandExecutor {
public boolean onCommand(CommandSender sender, Command cmd, String alias, String[] args) { public boolean onCommand(CommandSender sender, Command cmd, String alias, String[] args) {
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
try { writer.append("ArenaMode: ").append(Config.mode.name()).append('\n');
writer.append("ArenaMode: ").append(Config.mode.name()).append('\n'); writer.append("FightState: ").append(FightState.getFightState().name()).append('\n');
writer.append("FightState: ").append(FightState.getFightState().name()).append('\n'); writer.append("TechHider enabled: ").append(FightState.getStateDependentFeatures().get(FightSystem.getTechHider()).toString()).append('\n');
writer.append("TechHider enabled: ").append(FightState.getStateDependentFeatures().get(FightSystem.getTechHider()).toString()).append('\n');
writer.append("Arena region: ").append(Config.ArenaRegion.toString()).append('\n'); writer.append("Arena region: ").append(Config.ArenaRegion.toString()).append('\n');
writer.append("Team regions: "); writer.append("Team regions: ");
Fight.teams().forEach(t -> writer.append(t.getName()).append(':').append(t.getExtendRegion().toString()).append(' ')); Fight.teams().forEach(t -> writer.append(t.getName()).append(':').append(t.getExtendRegion().toString()).append(' '));
writer.append('\n'); writer.append('\n');
writer.append("HullHider regions: "); writer.append("HullHider regions: ");
FightSystem.getHullHider().getHullMap().forEach((t, h) -> writer.append(t.getName()).append(':').append(h.getRegion().toString()).append(' ')); FightSystem.getHullHider().getHullMap().forEach((t, h) -> writer.append(t.getName()).append(':').append(h.getRegion().toString()).append(' '));
writer.append('\n'); writer.append('\n');
writer.append("Hidden regions: "); writer.append("Hidden regions: ");
FightSystem.getTechHider().getHiddenRegion().forEach((p, r) -> writer.append(p.getName()).append(':').append(r.toString()).append(' ')); FightSystem.getTechHider().getHiddenRegion().forEach((p, r) -> writer.append(p.getName()).append(':').append(r.toString()).append(' '));
writer.append('\n'); writer.append('\n');
writer.append("TinyProtocol: "); writer.append('\n').append("Netty pipelines:\n");
writer.append(TinyProtocol.instance.toString()).append('\n'); Bukkit.getOnlinePlayers().forEach(p -> writer.append(p.getName()).append(": ").append(String.join(" ", TinyProtocol.instance.getPlayerInterceptors().get(p).getChannel().pipeline().names())).append('\n'));
writer.append('\n').append("Netty pipelines:\n");
Bukkit.getOnlinePlayers().forEach(p -> writer.append(p.getName()).append(": ").append(String.join(" ", TinyProtocol.instance.getPlayerInterceptors().get(p).getChannel().pipeline().names())).append('\n'));
} catch (Exception e) {
writer.append("Error while generating bug report: ").append(e.getMessage()).append('\n');
Bukkit.getLogger().log(Level.SEVERE, "Error while generating bug report", e);
}
SWException.log("Techhider-Bug reported by " + sender.getName() + ": " + Arrays.toString(args), writer.toString()); SWException.log("Techhider-Bug reported by " + sender.getName() + ": " + Arrays.toString(args), writer.toString());
return false; return false;
@@ -29,15 +29,11 @@ import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.StateDependentListener; import de.steamwar.fightsystem.states.StateDependentListener;
import de.steamwar.fightsystem.states.StateDependentTask; import de.steamwar.fightsystem.states.StateDependentTask;
import de.steamwar.fightsystem.utils.ItemBuilder; import de.steamwar.fightsystem.utils.ItemBuilder;
import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.block.Action; import org.bukkit.event.block.Action;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryOpenEvent;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@@ -111,19 +107,6 @@ public class HotbarKit extends Kit {
Player player = event.getPlayer(); Player player = event.getPlayer();
int slot = player.getInventory().getHeldItemSlot(); int slot = player.getInventory().getHeldItemSlot();
click(player, slot, event);
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
int slot = event.getSlot();
if (slot < 0 || slot >= HOTBAR_SIZE) return;
Player player = (Player) event.getWhoClicked();
click(player, slot, event);
}
private void click(Player player, int slot, Cancellable event) {
Kit activeKit = activeKits.get(player); Kit activeKit = activeKits.get(player);
if(!(activeKit instanceof HotbarKit) || PersonalKitCreator.inKitCreator(player) || activeKit.getInventory()[slot] == null) if(!(activeKit instanceof HotbarKit) || PersonalKitCreator.inKitCreator(player) || activeKit.getInventory()[slot] == null)
return; return;
-10
View File
@@ -39,13 +39,3 @@ dependencies {
implementation(project(":FightSystem:FightSystem_20")) implementation(project(":FightSystem:FightSystem_20"))
implementation(project(":FightSystem:FightSystem_21")) implementation(project(":FightSystem:FightSystem_21"))
} }
tasks.register<FightServer>("WarGear20") {
group = "run"
description = "Run a WarGear 1.20 Fight Server"
dependsOn(":SpigotCore:shadowJar")
dependsOn(":FightSystem:shadowJar")
template = "WarGear20"
worldName = "arenas/Pentraki"
config = "WarGear20.yml"
}
@@ -67,7 +67,7 @@ public class JumpAndRun extends BasicListener {
int count = CLICKED_COUNT.getOrDefault(player, -1); int count = CLICKED_COUNT.getOrDefault(player, -1);
if (count >= 0) { if (count >= 0) {
if (count > 20) { if (count > 60) {
toReset.add(player); toReset.add(player);
return; return;
} }
@@ -104,18 +104,12 @@ public class JumpAndRun extends BasicListener {
return; return;
} }
Vector point = points.get(index); Vector point = points.get(index);
if (location.getY() < point.getY()) index = -2; if (location.getY() < point.getY()) {
if (location.toVector().distanceSquared(point) >= 17) index = -2; return;
if (index > 0 && index < points.size() - 1) { }
Vector nextPoint = points.get(index + 1); if (location.toVector().distanceSquared(point) >= 12.25) {
if (location.getY() >= nextPoint.getY() && location.toVector().distanceSquared(nextPoint) < 17) {
index = index + 1;
}
}
if (index == -2) {
return; return;
} }
CURRENT_POS.put(event.getPlayer(), index); CURRENT_POS.put(event.getPlayer(), index);
event.getPlayer().playSound(event.getPlayer().getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.4F, 1); event.getPlayer().playSound(event.getPlayer().getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 0.4F, 1);
if (index < points.size() - 1) { if (index < points.size() - 1) {
@@ -71,17 +71,6 @@ public class TinyProtocol implements Listener {
@Getter @Getter
private final Map<Player, PacketInterceptor> playerInterceptors = new HashMap<>(); private final Map<Player, PacketInterceptor> playerInterceptors = new HashMap<>();
@Override
public String toString() {
return "TinyProtocol{" +
"plugin=" + plugin +
", connections=" + connections +
", closed=" + closed +
", packetFilters=" + packetFilters +
", playerInterceptors=" + playerInterceptors +
'}';
}
private TinyProtocol(final Plugin plugin) { private TinyProtocol(final Plugin plugin) {
this.plugin = plugin; this.plugin = plugin;
this.connections = networkManagers.get(getServerConnection(plugin)); this.connections = networkManagers.get(getServerConnection(plugin));
@@ -95,7 +84,6 @@ public class TinyProtocol implements Listener {
@EventHandler(priority = EventPriority.LOWEST) @EventHandler(priority = EventPriority.LOWEST)
public void onPlayerLogin(PlayerLoginEvent e) { public void onPlayerLogin(PlayerLoginEvent e) {
plugin.getLogger().info("Creating PacketInterceptor for: " + e.getPlayer().getName() + " (" + closed + ")");
if(closed) if(closed)
return; return;
new PacketInterceptor(e.getPlayer()); new PacketInterceptor(e.getPlayer());
@@ -130,8 +118,6 @@ public class TinyProtocol implements Listener {
} }
public final void close() { public final void close() {
plugin.getLogger().log(Level.INFO, "Closing PacketInterceptor", new Exception("Stacktrace"));
if(closed) if(closed)
return; return;
closed = true; closed = true;
@@ -171,7 +157,6 @@ public class TinyProtocol implements Listener {
synchronized (playerInterceptors) { synchronized (playerInterceptors) {
playerInterceptors.put(player, this); playerInterceptors.put(player, this);
} }
plugin.getLogger().info("Adding Techhider for: " + player.getName());
try { try {
channel.pipeline().addBefore("packet_handler", HANDLER_NAME, this); channel.pipeline().addBefore("packet_handler", HANDLER_NAME, this);
@@ -24,6 +24,7 @@ import de.steamwar.Reflection;
import de.steamwar.command.*; import de.steamwar.command.*;
import de.steamwar.core.authlib.AuthlibInjector; import de.steamwar.core.authlib.AuthlibInjector;
import de.steamwar.core.events.*; import de.steamwar.core.events.*;
import de.steamwar.inventory.ui.test.TestInvCommand;
import de.steamwar.message.Message; import de.steamwar.message.Message;
import de.steamwar.network.NetworkReceiver; import de.steamwar.network.NetworkReceiver;
import de.steamwar.network.handlers.ServerDataHandler; import de.steamwar.network.handlers.ServerDataHandler;
@@ -67,6 +68,7 @@ public class Core extends JavaPlugin{
@Override @Override
public void onEnable() { public void onEnable() {
new TestInvCommand();
errorHandler = new ErrorHandler(); errorHandler = new ErrorHandler();
crashDetector = new CrashDetector(); crashDetector = new CrashDetector();
@@ -0,0 +1,76 @@
/*
* 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.inventory.ui;
import de.steamwar.inventory.ui.util.UpdateStream;
import org.bukkit.entity.Player;
public abstract class StatefulUIInventory<T> extends UIInventory implements UpdateStream.Subscriber<T> {
private T state;
private final UpdateStream<T> stream;
private UpdateStream.Unsubscriber unsubscriber;
protected abstract UIComponent render(T state);
protected abstract void update(T state);
public StatefulUIInventory(UpdateStream<T> stateObservable, int playerSlots) {
super(playerSlots);
this.stream = stateObservable;
}
public StatefulUIInventory(UpdateStream<T> stateObservable) {
super();
this.stream = stateObservable;
}
public void dispose() {
unsubscriber.unsubscribe();
this.unsubscriber = null;
}
@Override
public UIComponent render() {
return render(state);
}
@Override
public void onUpdate(T current) {
state = current;
update(current);
updateUI();
}
@Override
void onClose() {
if (viewers.isEmpty()) {
dispose();
}
}
@Override
public void addViewer(Player... players) {
if (viewers.isEmpty()) {
unsubscriber = stream.subscribe(this);
}
super.addViewer(players);
}
}
@@ -0,0 +1,82 @@
/*
* 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.inventory.ui;
import de.steamwar.inventory.ui.item.Enchanted;
import de.steamwar.inventory.ui.item.OnClick;
import de.steamwar.inventory.ui.layout.FixedSizeContainer;
import de.steamwar.inventory.ui.util.*;
import org.bukkit.event.inventory.InventoryClickEvent;
import java.util.function.Consumer;
import java.util.function.Function;
public interface UIComponent {
ComponentRender render(RenderContext context);
default UIComponent onClick(Consumer<InventoryClickEvent> event) {
return onClick(false, event);
}
default UIComponent onClick(boolean allowDoubleClick, Consumer<InventoryClickEvent> event) {
return new OnClick(this, allowDoubleClick, inventoryClickEvent -> {
event.accept(inventoryClickEvent);
return true;
});
}
default UIComponent onClickWithUpdate(Function<InventoryClickEvent, Boolean> event) {
return onClickWithUpdate(false, event);
}
default UIComponent onClickWithUpdate(boolean allowDoubleClick, Function<InventoryClickEvent, Boolean> event) {
return new OnClick(this, allowDoubleClick, event);
}
default UIComponent onClickNoUpdate(Consumer<InventoryClickEvent> event) {
return onClickNoUpdate(false, event);
}
default UIComponent onClickNoUpdate(boolean allowDoubleClick, Consumer<InventoryClickEvent> event) {
return new OnClick(this, allowDoubleClick, inventoryClickEvent -> {
event.accept(inventoryClickEvent);
return false;
});
}
default UIComponent cancelOnClick() {
return new OnClick(this, true, inventoryClickEvent -> false);
}
default UIComponent enchanted() {
return new Enchanted(this);
}
default UIComponent enchanted(boolean enchanted) {
if (enchanted)
return enchanted();
else
return this;
}
default UIComponent size(Size size) {
return new FixedSizeContainer(size, this);
}
}
@@ -0,0 +1,33 @@
/*
* 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.inventory.ui;
import de.steamwar.inventory.ui.util.ComponentRender;
import de.steamwar.inventory.ui.util.RenderContext;
public abstract class UIFragment implements UIComponent {
abstract public UIComponent build(RenderContext context);
@Override
public ComponentRender render(RenderContext context) {
return build(context).render(context);
}
}
@@ -0,0 +1,242 @@
/*
* 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.inventory.ui;
import de.steamwar.core.Core;
import de.steamwar.inventory.ui.util.ComponentRender;
import de.steamwar.inventory.ui.util.RenderContext;
import de.steamwar.inventory.ui.util.RenderItem;
import de.steamwar.inventory.ui.util.Size;
import de.steamwar.message.Message;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryView;
import org.bukkit.inventory.ItemStack;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
public abstract class UIInventory {
protected final List<Player> viewers = new ArrayList<>();
private final Map<Player, OpenPlayerInventory> callbacks = new HashMap<>();
private final Map<Player, ItemStack[]> originalItems = new HashMap<>();
private UIInventory parent;
private Player currentRender;
private final int playerSlots;
private List<MemoHookMeta<?>> memoHooks = new ArrayList<>();
private int buildHookCount = 0;
protected UIInventory() {
this(0);
}
protected UIInventory(int playerSlots) {
this.playerSlots = playerSlots;
}
public abstract UIComponent render();
public abstract Inventory createInventory(Player player);
public abstract Message getMessages();
void onClose() { }
public <T> T useMemo(Supplier<T> supplier, List<Object> dependencies) {
int currentHook = buildHookCount++;
if (currentHook >= memoHooks.size()) {
T value = supplier.get();
memoHooks.add(new MemoHookMeta<>(dependencies, value));
return value;
}
MemoHookMeta<T> hook = (MemoHookMeta<T>) memoHooks.get(currentHook);
if (!hook.getDependencies().equals(dependencies)) {
hook.setDependencies(dependencies);
T value = supplier.get();
hook.setValue(value);
return value;
}
return hook.getValue();
}
public <T> T useMemo(Supplier<T> supplier) {
return useMemo(supplier, Collections.emptyList());
}
public void addViewer(Player... players) {
viewers.addAll(Arrays.asList(players));
for (Player player : players) {
openInventory(player);
}
}
private void openInventory(Player player) {
if (playerSlots > 0) {
originalItems.put(player, player.getInventory().getContents());
}
Inventory inventory = createInventory(player);
renderPlayer(player, inventory);
InventoryView view = player.openInventory(inventory);
Core.getInstance().getLogger().info("[UIINV] Opened " + view.getTitle() + " for " + player.getName());
}
private void renderPlayer(Player player, Inventory inventory) {
int width = Math.min(inventory.getSize(), 9);
if (width < 9 && playerSlots > 0) {
throw new IllegalArgumentException("Player Inventory is only supported in Chests");
}
int inventoryHeight = Math.max(inventory.getSize() / 9, 1);
int height = inventoryHeight + playerSlots;
int invSize = inventoryHeight * width;
currentRender = player;
buildHookCount = 0;
ComponentRender render = render().render(new RenderContext(new Size(width, height), new Size(width, height), this));
buildHookCount = -1;
currentRender = null;
if (render.getSize().getWidth() > width || render.getSize().getHeight() > height) {
throw new IllegalArgumentException("The UIComponent does not fit in the inventory");
}
Message message = getMessages();
ItemStack[] itemStacks = new ItemStack[inventory.getSize()];
ItemStack[] playerStacks = new ItemStack[playerSlots > 0 ? 4 * 9 : 0];
for (RenderItem item : render.getItems()) {
int slot = item.getX() + item.getY() * 9;
if (slot < invSize) {
itemStacks[item.getX() + item.getY() * 9] = item.getItemStack().message(message).build(player);
} else {
playerStacks[(item.getX() + item.getY() * 9) - invSize] = item.getItemStack().message(message).build(player);
}
}
inventory.setContents(itemStacks);
if (playerSlots > 0) {
ItemStack[] lastRow = new ItemStack[9];
System.arraycopy(playerStacks, playerStacks.length - 9, lastRow, 0, 9);
System.arraycopy(playerStacks, 0, playerStacks, 9, playerStacks.length - 9);
System.arraycopy(lastRow, 0, playerStacks, 0, 9);
// TODO: Update to Client-Side Only Update
player.getInventory().setContents(playerStacks);
}
Map<Integer, Function<InventoryClickEvent, Boolean>> clickEventMap = render.getItems().stream()
.filter(item -> item.getClickEvent() != null)
.collect(Collectors.toMap(renderItem -> renderItem.getX() + renderItem.getY() * 9, RenderItem::getClickEvent));
callbacks.computeIfAbsent(player, p -> new OpenPlayerInventory(p, clickEventMap).register()).setCallbacks(clickEventMap);
}
protected void updateTitle(String title, Object... params) {
if (currentRender != null) {
currentRender.getOpenInventory().setTitle(getMessages().parse(title, currentRender, params));
}
}
protected void updateUI() {
viewers.forEach(player -> renderPlayer(player, player.getOpenInventory().getTopInventory()));
}
protected void push(Player player, UIInventory inventory) {
inventory.parent = this;
inventory.addViewer(player);
viewers.remove(player);
}
protected void pop(Player player) {
if(parent == null)
return;
parent.addViewer(player);
}
@AllArgsConstructor
@Setter
private class OpenPlayerInventory implements Listener {
private final Player player;
private Map<Integer, Function<InventoryClickEvent, Boolean>> callbacks;
OpenPlayerInventory register() {
Core.getInstance().getServer().getPluginManager().registerEvents(this, Core.getInstance());
return this;
}
@EventHandler
public void onInventoryClick(InventoryClickEvent e) {
if (!player.equals(e.getWhoClicked()))
return;
if (callbacks.containsKey(e.getRawSlot()) && callbacks.get(e.getRawSlot()) != null) {
e.setCancelled(true);
Core.getInstance().getLogger().info("[UIINV] " + e.getWhoClicked().getName() + " " + e.getClick().name() + " clicked " + e.getRawSlot() + " on " + (e.getCurrentItem() != null ? e.getCurrentItem().getItemMeta().getDisplayName() : "[EMPTY]") + " in " + e.getView().getTitle());
if (callbacks.get(e.getRawSlot()).apply(e)) {
updateUI();
}
}
}
@EventHandler
public void onInventoryClose(InventoryCloseEvent e){
if(!player.equals(e.getPlayer()))
return;
InventoryClickEvent.getHandlerList().unregister(this);
InventoryCloseEvent.getHandlerList().unregister(this);
Core.getInstance().getLogger().info("[UIINV] " + player.getName() + " closed " + e.getView().getTitle());
if(callbacks.containsKey(-1))
callbacks.get(-1).apply(null);
viewers.remove(player);
UIInventory.this.callbacks.remove(player);
if (playerSlots > 0) {
player.getInventory().setContents(originalItems.remove(player));
}
onClose();
}
}
@Getter
@Setter
@AllArgsConstructor
private class MemoHookMeta<T> {
private List<Object> dependencies;
private T value;
}
}
@@ -0,0 +1,66 @@
/*
* 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.inventory.ui.fragments;
import de.steamwar.inventory.ui.UIComponent;
import de.steamwar.inventory.ui.UIFragment;
import de.steamwar.inventory.ui.item.ItemBuilder;
import de.steamwar.inventory.ui.item.UIItem;
import de.steamwar.inventory.ui.layout.Row;
import de.steamwar.inventory.ui.util.RenderContext;
import de.steamwar.inventory.ui.util.Size;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Material;
import java.util.Arrays;
import java.util.Map;
@Getter
@Setter
public class ActionBar extends UIFragment {
private static final UIComponent DEFAULT_FILLER = new UIItem(ItemBuilder.of(Material.GRAY_STAINED_GLASS_PANE).displayName("§e")).cancelOnClick();
private final Map<Integer, UIComponent> actionBar;
private UIComponent filler = DEFAULT_FILLER;
public ActionBar(Map<Integer, UIComponent> actionBar) {
this.actionBar = actionBar;
}
public ActionBar filler(UIComponent filler) {
this.filler = filler;
return this;
}
@Override
public UIComponent build(RenderContext context) {
UIComponent[] components = context.useMemo(() -> {
UIComponent[] cmp = new UIComponent[context.getContainerSize().getWidth()];
for (int i = 0; i < cmp.length; i++) {
cmp[i] = actionBar.getOrDefault(i, filler);
}
return cmp;
}, Arrays.asList(actionBar));
return new Row(Arrays.asList(components)).size(new Size(context.getContainerSize().getWidth(), 1));
}
}
@@ -0,0 +1,97 @@
/*
* 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.inventory.ui.fragments;
import de.steamwar.core.Core;
import de.steamwar.inventory.ui.UIComponent;
import de.steamwar.inventory.ui.UIFragment;
import de.steamwar.inventory.ui.item.ItemBuilder;
import de.steamwar.inventory.ui.item.UIItem;
import de.steamwar.inventory.ui.layout.Column;
import de.steamwar.inventory.ui.layout.FixedSizeContainer;
import de.steamwar.inventory.ui.layout.Row;
import de.steamwar.inventory.ui.util.RenderContext;
import lombok.Setter;
import org.bukkit.DyeColor;
import java.util.*;
@Setter
public class HorizontalScrollLayout extends UIFragment {
private List<UIComponent> data;
private int scroll = 0;
private final Map<Integer, UIComponent> actionBar = new HashMap<>();
public HorizontalScrollLayout(List<UIComponent> data) {
this.data = data;
}
private int maxScroll() {
return Math.max(0, Math.min(scroll, data.size() - 9 + 1));
}
public HorizontalScrollLayout actionBar(Map<Integer, UIComponent> actionBar) {
this.actionBar.clear();
this.actionBar.putAll(actionBar);
return this;
}
public HorizontalScrollLayout data(List<UIComponent> data) {
this.data = data;
return this;
}
@Override
public UIComponent build(RenderContext context) {
scroll = maxScroll();
boolean hasPrev = scroll > 0;
boolean hasNext = data.size() - scroll > 9;
Map<Integer, UIComponent> actionBar = context.useMemo(() -> {
Map<Integer, UIComponent> content = new HashMap<>(this.actionBar);
content.put(0, new UIItem(ItemBuilder.color(hasPrev ? DyeColor.LIME : DyeColor.GRAY).message(Core.MESSAGE).translateName(hasPrev ? "SWLISINV_PREVIOUS_PAGE_ACTIVE" : "SWLISINV_PREVIOUS_PAGE_INACTIVE")).onClickWithUpdate(inventoryClickEvent -> {
if (scroll > 0) {
scroll = Math.max(0, scroll - 9);
return true;
} else {
return false;
}
}));
content.put(8, new UIItem(ItemBuilder.color(hasNext ? DyeColor.LIME : DyeColor.GRAY).message(Core.MESSAGE).translateName(hasNext ? "SWLISINV_NEXT_PAGE_ACTIVE" : "SWLISINV_NEXT_PAGE_INACTIVE")).onClickWithUpdate(inventoryClickEvent -> {
if (hasNext) {
scroll = Math.min(scroll + 9, data.size() - 9);
return true;
} else {
return false;
}
}));
return content;
}, Arrays.asList(hasPrev, hasNext));
return new Column(
new FixedSizeContainer(context.getContainerSize().shrink(0, 1),
new Row(data.subList(scroll, Math.min(data.size(), scroll + 9)))
),
new ActionBar(actionBar)
);
}
}
@@ -0,0 +1,114 @@
/*
* 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.inventory.ui.fragments;
import de.steamwar.core.Core;
import de.steamwar.inventory.ui.UIComponent;
import de.steamwar.inventory.ui.UIFragment;
import de.steamwar.inventory.ui.item.ItemBuilder;
import de.steamwar.inventory.ui.item.UIItem;
import de.steamwar.inventory.ui.layout.Column;
import de.steamwar.inventory.ui.layout.FixedSizeContainer;
import de.steamwar.inventory.ui.layout.Grid;
import de.steamwar.inventory.ui.util.RenderContext;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.DyeColor;
import org.bukkit.event.inventory.ClickType;
import java.util.*;
import java.util.function.Consumer;
@Getter
@Setter
public class PageLayout<T> extends UIFragment {
private final List<UIListEntry<T>> items;
private final UIListCallback<T> callback;
private int page = 0;
private Consumer<Integer> onPageChange = null;
private Map<Integer, UIItem> itemsOnBar = Collections.emptyMap();
public PageLayout(List<UIListEntry<T>> items, UIListCallback<T> callback) {
System.out.println(items.size());
this.items = items;
this.callback = callback;
}
@Override
public UIComponent build(RenderContext context) {
int itemsOnPage = context.getContainerSize().getWidth() * (context.getContainerSize().getHeight() - 1);
List<UIComponent> uiItems = context.useMemo(() -> {
int i = page * itemsOnPage;
int ipageLimit = items.size() - page * itemsOnPage;
if (ipageLimit > itemsOnPage) {
ipageLimit = itemsOnPage;
}
List<UIComponent> pageItems = new ArrayList<>();
for (int ipage = 0; ipage < ipageLimit; ipage++) {
UIListEntry<T> e = items.get(i);
pageItems.add(new UIItem(e.item).onClickNoUpdate(inventoryClickEvent -> callback.clicked(inventoryClickEvent.getClick(), e.object)));
i++;
}
return pageItems;
}, Arrays.asList(page, items.size(), itemsOnPage));
Map<Integer, UIComponent> uiBar = context.useMemo(() -> {
Map<Integer, UIComponent> content = new HashMap<>(itemsOnBar);
if (page != 0) {
content.put(0, new UIItem(ItemBuilder.color(DyeColor.LIME).message(Core.MESSAGE).translateName("SWLISINV_PREVIOUS_PAGE_ACTIVE")).onClick(inventoryClickEvent -> page--));
} else {
content.put(0, new UIItem(ItemBuilder.color(DyeColor.GRAY).message(Core.MESSAGE).translateName("SWLISINV_PREVIOUS_PAGE_INACTIVE")).cancelOnClick());
}
if (page < items.size() / itemsOnPage - (items.size() % itemsOnPage == 0 ? 1 : 0)) {
content.put(8, new UIItem(ItemBuilder.color(DyeColor.LIME).message(Core.MESSAGE).translateName("SWLISINV_NEXT_PAGE_ACTIVE")).onClick(inventoryClickEvent -> page++));
} else {
content.put(8, new UIItem(ItemBuilder.color(DyeColor.GRAY).message(Core.MESSAGE).translateName("SWLISINV_NEXT_PAGE_INACTIVE")).cancelOnClick());
}
return content;
}, Arrays.asList(page, items.size(), itemsOnPage));
return new Column(
new FixedSizeContainer(context.getContainerSize().shrink(0, 1),
new Grid(uiItems)),
new ActionBar(uiBar)
);
}
public interface UIListCallback<T>{
void clicked(ClickType click, T element);
}
@Data
public static class UIListEntry<T>{
final ItemBuilder item;
final T object;
}
}
@@ -0,0 +1,44 @@
/*
* 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.inventory.ui.item;
import de.steamwar.inventory.ui.UIComponent;
import de.steamwar.inventory.ui.util.ComponentRender;
import de.steamwar.inventory.ui.util.RenderContext;
import de.steamwar.inventory.ui.util.RenderItem;
public class Enchanted implements UIComponent {
private final UIComponent children;
public Enchanted(UIComponent children) {
this.children = children;
}
@Override
public ComponentRender render(RenderContext context) {
ComponentRender render = children.render(context);
for (RenderItem item : render.getItems()) {
item.getItemStack().enchanted();
}
return render;
}
}
@@ -0,0 +1,205 @@
/*
* 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.inventory.ui.item;
import de.steamwar.core.FlatteningWrapper;
import de.steamwar.core.TrickyTrialsWrapper;
import de.steamwar.inventory.SWItem;
import de.steamwar.message.Message;
import de.steamwar.message.SubMessage;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import java.util.*;
import java.util.stream.Collectors;
public class ItemBuilder {
private static final Map<DyeColor, Integer> DYE_COLOR_INTEGER_MAP = new EnumMap<>(DyeColor.class);
static {
DYE_COLOR_INTEGER_MAP.put(DyeColor.BLACK, 16);
DYE_COLOR_INTEGER_MAP.put(DyeColor.RED, 1);
DYE_COLOR_INTEGER_MAP.put(DyeColor.GREEN, 2);
DYE_COLOR_INTEGER_MAP.put(DyeColor.BROWN, 3);
DYE_COLOR_INTEGER_MAP.put(DyeColor.BLUE, 4);
DYE_COLOR_INTEGER_MAP.put(DyeColor.PURPLE, 5);
DYE_COLOR_INTEGER_MAP.put(DyeColor.CYAN, 6);
DYE_COLOR_INTEGER_MAP.put(DyeColor.LIGHT_GRAY, 7);
DYE_COLOR_INTEGER_MAP.put(DyeColor.GRAY, 8);
DYE_COLOR_INTEGER_MAP.put(DyeColor.PINK, 9);
DYE_COLOR_INTEGER_MAP.put(DyeColor.LIME, 10);
DYE_COLOR_INTEGER_MAP.put(DyeColor.YELLOW, 11);
DYE_COLOR_INTEGER_MAP.put(DyeColor.LIGHT_BLUE, 12);
DYE_COLOR_INTEGER_MAP.put(DyeColor.MAGENTA, 13);
DYE_COLOR_INTEGER_MAP.put(DyeColor.ORANGE, 14);
DYE_COLOR_INTEGER_MAP.put(DyeColor.WHITE, 15);
}
private final ItemStack itemStack;
private final ItemMeta itemMeta;
private Message message = null;
private SubMessage translateName = null;
private List<SubMessage> translateLore = null;
private ItemBuilder(Material material) {
this(new ItemStack(material));
}
private ItemBuilder(ItemStack item) {
this.itemStack = item;
this.itemMeta = itemStack.getItemMeta();
}
public static ItemBuilder of(Material material) {
return new ItemBuilder(material);
}
public static ItemBuilder of(ItemStack itemStack) {
return new ItemBuilder(itemStack.getType()).amount(itemStack.getAmount());
}
private static ItemBuilder of(Material material, Byte meta) {
ItemStack itemStack;
try {
itemStack = new ItemStack(material, 1, (short)0, meta);
} catch (IllegalArgumentException e) {
itemStack = new ItemStack(material, 1);
}
return new ItemBuilder(itemStack);
}
public static ItemBuilder color(DyeColor color) {
return ItemBuilder.of(SWItem.getDye(DYE_COLOR_INTEGER_MAP.get(color)), DYE_COLOR_INTEGER_MAP.get(color).byteValue());
}
public static ItemBuilder skull(OfflinePlayer player) {
return skull(player.getName());
}
public static ItemBuilder skull(String name) {
return ItemBuilder.of(FlatteningWrapper.impl.setSkullOwner(name));
}
public ItemBuilder message(Message message) {
if (this.message == null) {
this.message = message;
}
return this;
}
public ItemStack build(Player player) {
if (message != null) {
if (translateName != null) {
itemMeta.setDisplayName(message.parse(translateName.getMessage(), player, translateName.getParams()));
}
if (translateLore != null) {
itemMeta.setLore(translateLore.stream().map(subMessage -> message.parse(subMessage.getMessage(), player, subMessage.getParams())).collect(Collectors.toList()));
}
}
itemStack.setItemMeta(itemMeta);
return itemStack;
}
public ItemBuilder amount(int amount) {
itemStack.setAmount(amount);
return this;
}
public ItemBuilder displayName(String name) {
itemMeta.setDisplayName(name);
return this;
}
public ItemBuilder lore(String... lore) {
if (itemMeta.hasLore()) {
List<String> loreList = itemMeta.getLore();
loreList.addAll(Arrays.asList(lore));
itemMeta.setLore(loreList);
} else {
itemMeta.setLore(Arrays.asList(lore));
}
return this;
}
public ItemBuilder translateName(String name, Object... params) {
this.translateName = new SubMessage(name, params);
return this;
}
public ItemBuilder addTranslateLore(SubMessage... lore) {
if (this.translateLore == null) {
this.translateLore = Arrays.asList(lore);
} else {
this.translateLore.addAll(Arrays.asList(lore));
}
return this;
}
public ItemBuilder enchanted() {
itemMeta.addEnchant(TrickyTrialsWrapper.impl.getUnbreakingEnchantment(), 10, true);
return this;
}
public ItemBuilder enchanted(boolean enchanted) {
if(enchanted) {
return enchanted();
} else {
return this;
}
}
public ItemBuilder hideFlags() {
for (ItemFlag flag : EnumSet.allOf(ItemFlag.class)) {
itemMeta.addItemFlags(flag);
}
return this;
}
public ItemBuilder customModel(int modelData) {
itemMeta.setCustomModelData(modelData);
return this;
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
ItemBuilder that = (ItemBuilder) o;
return Objects.equals(itemStack, that.itemStack) && Objects.equals(itemMeta, that.itemMeta) && Objects.equals(message, that.message) && Objects.equals(translateName, that.translateName) && Objects.equals(translateLore, that.translateLore);
}
@Override
public String toString() {
return "ItemBuilder{" +
"itemStack=" + itemStack +
", itemMeta=" + itemMeta +
", message=" + message +
", translateName=" + translateName +
", translateLore=" + translateLore +
'}';
}
}
@@ -0,0 +1,74 @@
/*
* 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.inventory.ui.item;
import de.steamwar.inventory.ui.UIComponent;
import de.steamwar.inventory.ui.util.ComponentRender;
import de.steamwar.inventory.ui.util.RenderContext;
import de.steamwar.inventory.ui.util.RenderItem;
import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryClickEvent;
import java.util.function.Function;
public class OnClick implements UIComponent {
private final UIComponent children;
private final boolean allowDoubleClick;
private final Function<InventoryClickEvent, Boolean> event;
public OnClick(UIComponent children, boolean allowDoubleClick, Function<InventoryClickEvent, Boolean> event) {
this.children = children;
this.allowDoubleClick = allowDoubleClick;
this.event = event;
}
@Override
public ComponentRender render(RenderContext context) {
ComponentRender render = children.render(context);
for (RenderItem item : render.getItems()) {
item.setClickEvent(ev -> {
if (ev.getClick() != ClickType.DOUBLE_CLICK || allowDoubleClick) {
return event.apply(ev);
}
return false;
});
}
return render;
}
@Override
public String toString() {
return "OnClick{" +
"children=" + children +
", allowDoubleClick=" + allowDoubleClick +
'}';
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
OnClick onClick = (OnClick) o;
return allowDoubleClick == onClick.allowDoubleClick && children.equals(onClick.children);
}
}
@@ -0,0 +1,42 @@
/*
* 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.inventory.ui.item;
import de.steamwar.inventory.ui.UIComponent;
import de.steamwar.inventory.ui.util.ComponentRender;
import de.steamwar.inventory.ui.util.RenderContext;
import de.steamwar.inventory.ui.util.Size;
import java.util.ArrayList;
public class Space implements UIComponent {
@Override
public ComponentRender render(RenderContext context) {
return new ComponentRender(
new ArrayList<>(),
new Size(1, 1)
);
}
@Override
public boolean equals(Object obj) {
return obj instanceof Space;
}
}
@@ -0,0 +1,64 @@
/*
* 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.inventory.ui.item;
import de.steamwar.inventory.ui.UIComponent;
import de.steamwar.inventory.ui.util.*;
import lombok.EqualsAndHashCode;
import org.bukkit.inventory.ItemStack;
import java.util.Arrays;
import java.util.Objects;
public class UIItem implements UIComponent {
private final ItemBuilder itemStack;
public UIItem(ItemStack itemStack) {
this(ItemBuilder.of(itemStack));
}
public UIItem(ItemBuilder itemStack) {
this.itemStack = itemStack;
}
@Override
public ComponentRender render(RenderContext context) {
return new ComponentRender(
Arrays.asList(new RenderItem(0, 0, itemStack, null)),
new Size(1, 1)
);
}
@Override
public String toString() {
return "UIItem{" +
"itemStack=" + itemStack +
'}';
}
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
UIItem uiItem = (UIItem) o;
return Objects.equals(itemStack, uiItem.itemStack);
}
}
@@ -0,0 +1,27 @@
/*
* 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.inventory.ui.layout;
public enum Alignment {
START,
CENTER,
END,
BETWEEN
}
@@ -0,0 +1,93 @@
/*
* 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.inventory.ui.layout;
import de.steamwar.inventory.ui.UIComponent;
import de.steamwar.inventory.ui.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Column implements UIComponent {
private final List<UIComponent> components;
private Alignment alignment;
private boolean shrinkWrap = false;
public Column(UIComponent... components) {
this(Arrays.asList(components));
}
public Column(List<UIComponent> components) {
this.components = components;
}
public Column centered() {
this.alignment = Alignment.CENTER;
return this;
}
public Column setShrinkWrap(boolean shrinkWrap) {
this.shrinkWrap = shrinkWrap;
return this;
}
@Override
public ComponentRender render(RenderContext context) {
List<RenderItem> renderItems = new ArrayList<>();
int height = 0;
int maxWidth = 0;
for (UIComponent component : components) {
ComponentRender render = component.render(context.withContainerSize(context.getContainerSize().withHeight(context.getContainerSize().getHeight() - height)));
for(RenderItem item : render.getItems()){
renderItems.add(item.shift(0, height));
}
maxWidth = Math.max(maxWidth, render.getSize().getWidth());
height += render.getSize().getHeight();
}
if (alignment == Alignment.CENTER) {
int shiftAmount = (context.getContainerSize().getHeight() - height) / 2;
for (RenderItem item : renderItems) {
item.shift(0, shiftAmount);
}
} else if (alignment == Alignment.BETWEEN) {
int shiftAmount = (context.getContainerSize().getHeight() - height) / (components.size() - 1);
for (int i = 0; i < components.size() - 1; i++) {
RenderItem item = renderItems.get(i);
item.shift(0, shiftAmount * i);
}
} else if (alignment == Alignment.END) {
int shiftAmount = (context.getContainerSize().getHeight() - height);
for (RenderItem item : renderItems) {
item.shift(0, shiftAmount);
}
}
return new ComponentRender(
renderItems,
new Size(maxWidth, shrinkWrap ? height : context.getContainerSize().getHeight())
);
}
}
@@ -0,0 +1,40 @@
/*
* 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.inventory.ui.layout;
import de.steamwar.inventory.ui.UIComponent;
import de.steamwar.inventory.ui.util.ComponentRender;
import de.steamwar.inventory.ui.util.RenderContext;
import de.steamwar.inventory.ui.util.Size;
public class FixedSizeContainer implements UIComponent {
private final UIComponent children;
private final Size size;
public FixedSizeContainer(Size size, UIComponent children) {
this.children = children;
this.size = size;
}
@Override
public ComponentRender render(RenderContext context) {
return children.render(context.withContainerSize(size)).withSize(size);
}
}
@@ -0,0 +1,68 @@
/*
* 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.inventory.ui.layout;
import de.steamwar.inventory.ui.UIComponent;
import de.steamwar.inventory.ui.util.ComponentRender;
import de.steamwar.inventory.ui.util.RenderContext;
import de.steamwar.inventory.ui.util.RenderItem;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Grid implements UIComponent {
private final List<UIComponent> children;
public Grid(UIComponent... children) {
this(Arrays.asList(children));
}
public Grid(List<UIComponent> children) {
this.children = children;
}
@Override
public ComponentRender render(RenderContext context) {
List<RenderItem> renderItems = new ArrayList<>();
int x = 0;
int y = 0;
int currentRowMaxHeight = 0;
for (UIComponent child : children) {
ComponentRender render = child.render(context.withContainerSize(
context.getContainerSize()
.withWidth(context.getContainerSize().getWidth() - x)
.withHeight(context.getContainerSize().getHeight() - y)));
currentRowMaxHeight = Math.max(currentRowMaxHeight, render.getSize().getHeight());
for (RenderItem item : render.getItems()) {
renderItems.add(item.shift(x, y));
}
x += render.getSize().getWidth();
if(x >= context.getContainerSize().getWidth()){
x = 0;
y += currentRowMaxHeight;
currentRowMaxHeight = 0;
}
}
return new ComponentRender(renderItems, context.getContainerSize());
}
}
@@ -0,0 +1,98 @@
/*
* 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.inventory.ui.layout;
import de.steamwar.inventory.ui.UIComponent;
import de.steamwar.inventory.ui.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Row implements UIComponent {
private final List<UIComponent> components;
private Alignment alignment;
private boolean shrinkWrap = false;
public Row(UIComponent... components) {
this(Arrays.asList(components));
}
public Row(List<UIComponent> components) {
this.components = components;
}
public Row centered() {
this.alignment = Alignment.CENTER;
return this;
}
public Row alignment(Alignment alignment) {
this.alignment = alignment;
return this;
}
public Row setShrinkWrap(boolean shrinkWrap) {
this.shrinkWrap = shrinkWrap;
return this;
}
@Override
public ComponentRender render(RenderContext context) {
List<RenderItem> renderItems = new ArrayList<>();
int width = 0;
int maxHeight = 0;
for (UIComponent component : components) {
ComponentRender render = component.render(context.withContainerSize(context.getContainerSize().withWidth(context.getContainerSize().getWidth() - width)));
for(RenderItem item : render.getItems()){
renderItems.add(item.shift(width, 0));
}
maxHeight = Math.max(maxHeight, render.getSize().getHeight());
width += render.getSize().getWidth();
}
if (alignment == Alignment.CENTER) {
int shiftAmount = (context.getContainerSize().getWidth() - width) / 2;
for (RenderItem item : renderItems) {
item.shift(shiftAmount, 0);
}
} else if (alignment == Alignment.BETWEEN) {
int shiftAmount = (context.getContainerSize().getWidth() - width) / (components.size() - 1);
for (int i = 0; i < components.size() - 1; i++) {
RenderItem item = renderItems.get(i);
item.shift(shiftAmount * i, 0);
}
} else if (alignment == Alignment.END) {
int shiftAmount = (context.getContainerSize().getWidth() - width);
for (RenderItem item : renderItems) {
item.shift(shiftAmount, 0);
}
}
return new ComponentRender(
renderItems,
new Size(shrinkWrap ? width : context.getContainerSize().getWidth(), maxHeight)
);
}
}
@@ -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.inventory.ui.test;
import de.steamwar.command.SWCommand;
import de.steamwar.inventory.ui.util.UpdateStream;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
public class TestInvCommand extends SWCommand {
public static final UpdateStream<List<Integer>> STREAM = new UpdateStream<>(new ArrayList<>());
public TestInvCommand() {
super("testinv");
}
@Register
public void run(Player player) {
new TestInventory(STREAM).addViewer(player);
}
}
@@ -0,0 +1,79 @@
/*
* 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.inventory.ui.test;
import de.steamwar.core.Core;
import de.steamwar.inventory.ui.StatefulUIInventory;
import de.steamwar.inventory.ui.UIComponent;
import de.steamwar.inventory.ui.fragments.HorizontalScrollLayout;
import de.steamwar.inventory.ui.item.ItemBuilder;
import de.steamwar.inventory.ui.item.UIItem;
import de.steamwar.inventory.ui.util.UpdateStream;
import de.steamwar.message.Message;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static de.steamwar.inventory.ui.test.TestInvCommand.STREAM;
public class TestInventory extends StatefulUIInventory<List<Integer>> {
private HorizontalScrollLayout scrollLayout;
private static int counter = 1;
public TestInventory(UpdateStream<List<Integer>> stateObservable) {
super(stateObservable);
scrollLayout = new HorizontalScrollLayout(new ArrayList<>());
}
@Override
protected UIComponent render(List<Integer> state) {
return scrollLayout;
}
@Override
protected void update(List<Integer> state) {
System.out.println("Update");
List<UIComponent> items = state.stream().map(integer -> new UIItem(ItemBuilder.of(Material.ACACIA_PLANKS).displayName(String.valueOf(integer))).onClickNoUpdate(inventoryClickEvent -> STREAM.update(current -> {
current.remove(integer);
return current;
}))).collect(Collectors.toList());
items.add(new UIItem(ItemBuilder.of(Material.BUCKET).displayName("Add")).onClickNoUpdate(inventoryClickEvent -> STREAM.update(current -> {
current.add(counter++);
return current;
})));
scrollLayout = scrollLayout.data(items);
}
@Override
public Inventory createInventory(Player player) {
return Bukkit.createInventory(null, 9 * 6, "Test Inventory");
}
@Override
public Message getMessages() {
return Core.MESSAGE;
}
}
@@ -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.inventory.ui.util;
import lombok.Data;
import lombok.With;
import java.util.List;
@Data
@With
public class ComponentRender {
private final List<RenderItem> items;
private final Size size;
}
@@ -0,0 +1,43 @@
/*
* 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.inventory.ui.util;
import de.steamwar.inventory.ui.UIInventory;
import lombok.Data;
import lombok.With;
import java.util.List;
import java.util.function.Supplier;
@Data
@With
public class RenderContext {
private final Size inventorySize;
private final Size containerSize;
private final UIInventory inventory;
public <T> T useMemo(Supplier<T> supplier, List<Object> dependencies) {
return inventory.useMemo(supplier, dependencies);
}
public <T> T useMemo(Supplier<T> supplier) {
return inventory.useMemo(supplier);
}
}
@@ -0,0 +1,42 @@
/*
* 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.inventory.ui.util;
import de.steamwar.inventory.ui.item.ItemBuilder;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.With;
import org.bukkit.event.inventory.InventoryClickEvent;
import java.util.function.Function;
@Data
@With
@AllArgsConstructor
public class RenderItem {
private int x;
private int y;
private ItemBuilder itemStack;
private Function<InventoryClickEvent, Boolean> clickEvent;
public RenderItem shift(int deltaX, int deltaY) {
return withX(x + deltaX).withY(y + deltaY);
}
}
@@ -0,0 +1,34 @@
/*
* 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.inventory.ui.util;
import lombok.Data;
import lombok.With;
@Data
@With
public class Size {
private final int width;
private final int height;
public Size shrink(int width, int height) {
return new Size(this.width - width, this.height - height);
}
}
@@ -0,0 +1,66 @@
/*
* 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.inventory.ui.util;
import java.util.ArrayList;
import java.util.List;
public class UpdateStream<T> {
private T current;
private final List<Subscriber<T>> subscribers = new ArrayList<>();
public UpdateStream(T current) {
this.current = current;
}
public Unsubscriber subscribe(Subscriber<T> subscriber) {
subscribers.add(subscriber);
subscriber.onUpdate(current);
return () -> subscribers.remove(subscriber);
}
public T get() {
return current;
}
public void emit(T newCurrent) {
current = newCurrent;
subscribers.forEach(subscriber -> subscriber.onUpdate(newCurrent));
}
public void update(Updater<T> updater) {
current = updater.update(current);
subscribers.forEach(subscriber -> subscriber.onUpdate(current));
}
public interface Subscriber<T> {
void onUpdate(T current);
}
public interface Unsubscriber {
void unsubscribe();
}
public interface Updater<T> {
T update(T current);
}
}
+1 -9
View File
@@ -66,12 +66,4 @@ dependencies {
implementation(libs.apolloprotos) implementation(libs.apolloprotos)
implementation(libs.nbt) implementation(libs.nbt)
} }
tasks.register<DevServer>("DevVelocity") {
group = "run"
description = "Run a Dev Velocity"
dependsOn(":VelocityCore:shadowJar")
dependsOn(":VelocityCore:Persistent:jar")
template = "DevVelocity"
}
@@ -44,7 +44,6 @@ public enum ServerVersion {
PAPER_18("paper-1.18.2.jar", 15, ProtocolVersion.MINECRAFT_1_18_2), PAPER_18("paper-1.18.2.jar", 15, ProtocolVersion.MINECRAFT_1_18_2),
PAPER_19("paper-1.19.3.jar", 19, ProtocolVersion.MINECRAFT_1_19_3), PAPER_19("paper-1.19.3.jar", 19, ProtocolVersion.MINECRAFT_1_19_3),
PAPER_20("paper-1.20.1.jar", 20, ProtocolVersion.MINECRAFT_1_20), PAPER_20("paper-1.20.1.jar", 20, ProtocolVersion.MINECRAFT_1_20),
DEVEL_21("paper-1.21.5.jar", 21, ProtocolVersion.MINECRAFT_1_21_5),
PAPER_21("paper-1.21.3.jar", 21, ProtocolVersion.MINECRAFT_1_21_2); PAPER_21("paper-1.21.3.jar", 21, ProtocolVersion.MINECRAFT_1_21_2);
private static final Map<String, ServerVersion> chatMap = new HashMap<>(); private static final Map<String, ServerVersion> chatMap = new HashMap<>();
@@ -95,10 +94,6 @@ public enum ServerVersion {
} }
public static ServerVersion get(int version) { public static ServerVersion get(int version) {
if (version == 21) {
return DEVEL_21;
}
return versionMap.get(version); return versionMap.get(version);
} }
@@ -149,23 +149,6 @@ fun Route.configureSchematic() {
return@let SchematicFormat.SPONGE_V2 return@let SchematicFormat.SPONGE_V2
} }
if (version == SchematicFormat.SPONGE_V3) {
try {
val fawe = schem.getCompound("Metadata")
.getCompound("WorldEdit")
.getString("Version")
.value
if (fawe.equals("2.12.3-SNAPSHOT")) {
SWException.log("Schematic with Bugged Version Uploaded", """
Schematic=$schemName
User=${user.userName}
Id=${user.id}
""".trimIndent())
}
} catch (_: Exception) {}
}
val data = NodeData(node.id, version) val data = NodeData(node.id, version)
data.saveFromStream(content.inputStream(), version) data.saveFromStream(content.inputStream(), version)
-214
View File
@@ -1,214 +0,0 @@
/*
* 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/>.
*/
plugins {
}
class DevServer extends DefaultTask {
@Input
@Optional
String worldName = null
@Input
String template = null
@Input
@Optional
String plugins = null
@Input
@Optional
Integer port = null
@Input
@Optional
String jar = null
@Input
@Optional
Map<String, String> dParams = new HashMap<>()
DevServer() {
super()
doFirst {
List<Project> projects = []
projects.add(project)
while (projects.first.parent != null) {
projects.add(0, projects.first.parent)
}
def properties = new Properties()
projects.forEach {
def file = new File(it.projectDir, "steamwar.properties")
if (file.exists()) {
properties.load(new FileInputStream(file))
}
}
if (worldName == null) worldName = properties.get("worldName")
host = properties.get("host")
if (host == null) {
throw new GradleException("Please supply the 'host' in a 'steamwar.properties' files either in this project dir or any parent project!")
}
}
doLast {
checkHasTemplate()
uploadDependencies()
startDevServer()
}
finalizedBy(new Finalizer())
}
@Internal
BufferedWriter processInput
@Internal
String host
@Internal
Boolean running = true
class Finalizer extends DefaultTask {
Finalizer() {
super()
doLast {
running = false
if (processInput != null) {
processInput.write(template.endsWith("Velocity") ? "end\n" : "stop\n")
processInput.flush()
}
}
}
}
void checkHasTemplate() {
def process = new ProcessBuilder("ssh", host, "-T", "ls $template").start()
process.waitFor()
if (new BufferedReader(new InputStreamReader(process.inputStream)).lines().count() < 4) {
throw new GradleException("Used template is not in your user.home directory of the given host $host")
}
}
void uploadDependencies() {
def base = plugins == null ? "$template/plugins" : plugins
println("Uploading to ~/$base")
this.dependsOn.forEach {
Project resolved
AbstractArchiveTask archiveTask
if (it instanceof String) {
resolved = project.findProject(it.substring(0, it.lastIndexOf(':')))
archiveTask = (AbstractArchiveTask) resolved.tasks.findByName(it.substring(it.lastIndexOf(':') + 1))
} else {
throw new GradleException("Illegal argument for uploading dependencies")
}
def archive = archiveTask.archiveFile.get().asFile
println("Uploading $archive")
new ProcessBuilder("ssh", host, "-T", "rm $base/${archive.name.replace("-all", "")}").start().waitFor()
new ProcessBuilder("scp", archive.absolutePath, "$host:~/$base/${archive.name.replace("-all", "")}").start().waitFor()
println("Uploaded $archive")
}
}
void startDevServer() {
def devPy = new StringBuilder().append("dev.py")
if (port != null) devPy.append(" --port $port")
if (worldName != null) devPy.append(" -w $template/$worldName")
if (plugins != null) devPy.append(" -p $plugins")
if (jar != null) devPy.append(" --jar $jar")
for (Map.Entry<String, String> dParam : dParams.entrySet()) {
devPy.append(" -D${dParam.key}=${dParam.value}")
}
devPy.append(" $template")
println("Starting $template with command ${devPy.toString()}")
def process = new ProcessBuilder("ssh", host, "-T", devPy.toString()).start()
def processOutput = new BufferedReader(new InputStreamReader(process.inputStream))
new Thread({
while (running) {
if (processOutput.ready()) {
println(processOutput.readLine())
}
}
}).start()
processInput = new BufferedWriter(new OutputStreamWriter(process.outputStream))
def input = new BufferedReader(new InputStreamReader(System.in))
new Thread({
while (running) {
def text = input.readLine()
if (text == null) break
processInput.write(text)
processInput.newLine()
processInput.flush()
}
}).start()
process.waitFor()
processInput = null
running = false
}
}
class FightServer extends DevServer {
@Input
@Optional
Integer checkSchemID = 0
@Input
@Optional
Integer prepareSchemID = 0
@Input
@Optional
Integer replay = 0
@Input
@Optional
String config = null
@Input
@Optional
// Property: fightID
Integer eventKampfID = 0
@Input
@Optional
UUID blueLeader = null
@Input
@Optional
UUID redLeader = null
FightServer() {
super()
doFirst {
if (checkSchemID != 0) dParams.put("checkSchemID", "$checkSchemID")
if (prepareSchemID != 0) dParams.put("prepareSchemID", "$prepareSchemID")
if (replay != 0) dParams.put("replay", "$replay")
if (eventKampfID != 0) dParams.put("fightID", "$eventKampfID")
if (blueLeader != null) dParams.put("blueLeader", blueLeader.toString())
if (redLeader != null) dParams.put("redLeader", redLeader.toString())
if (config != null) dParams.put("config", config)
}
}
}