Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
1cb2e70ede
|
|||
|
8ce25ec19f
|
|||
| a9fb982143 | |||
| 5b8d881e01 | |||
| eb55f4b395 | |||
| 273db91d06 | |||
| 983ad544c1 | |||
| a6a34b2221 | |||
| 6c6bd19038 | |||
| 89e05cd109 | |||
| ea9d7ac584 | |||
| 17e1cf53b0 | |||
| f64f337f17 | |||
| c5f5be7d58 | |||
| 9f18644763 | |||
| 1de17d27f4 | |||
| 3cd0db9bdf | |||
| 9711b48f2f | |||
|
9934b8bbb2
|
|||
| 6447265b90 | |||
| 71258318db | |||
| 52e95e2649 | |||
| de6ac2cf20 | |||
| 628001e693 | |||
| 47c148d64a | |||
| 78617d5a98 | |||
| 69b924ded6 | |||
| 002f7e5542 | |||
| 5d70f75ac9 | |||
| 7f21a31ec9 | |||
| 23b5ab3e82 | |||
| 4f9fe07951 | |||
| 3bcff4c5ce | |||
| 1810cb7546 | |||
| e55ca911c4 | |||
| 793f2de6c3 | |||
| 54fa47bd99 | |||
| 14a770b207 | |||
| 7b3a04f4eb | |||
| f2a06057a8 | |||
| e11f3f7cbc | |||
| ebc10c1ce4 | |||
| d682e35159 | |||
| 7d74eb0c09 | |||
| 7e18207b87 | |||
| 72b23ad116 | |||
| c508627d92 | |||
| 93ff982649 | |||
| e49cfa9495 | |||
|
a7adfe378f
|
|||
|
9aa363de6f
|
|||
|
5a5090c74d
|
|||
|
99ca7205fb
|
|||
| e85604fdc4 | |||
|
936ee38503
|
@@ -133,7 +133,12 @@ public class BauSystem extends JavaPlugin implements Listener {
|
||||
|
||||
Bukkit.getWorlds().get(0).setGameRule(GameRule.SEND_COMMAND_FEEDBACK, false);
|
||||
|
||||
String identifier = BauServerInfo.getOwnerUser().getUUID().toString().replace("-", "");
|
||||
String identifier;
|
||||
if (BauServerInfo.getWorldId() != null) {
|
||||
identifier = BauServerInfo.getWorldId().toString().replace("-", "");
|
||||
} else {
|
||||
identifier = BauServerInfo.getOwnerUser().getUUID().toString().replace("-", "");
|
||||
}
|
||||
WorldIdentifier.set("bau/" + Core.getVersion() + "/" + identifier);
|
||||
}
|
||||
|
||||
@@ -224,4 +229,4 @@ public class BauSystem extends JavaPlugin implements Listener {
|
||||
AtomicReference<BukkitTask> task = new AtomicReference<>();
|
||||
task.set(runTaskTimer(plugin, () -> consumer.accept(task.get()), delay, period));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,11 +60,16 @@ public enum Permission {
|
||||
}
|
||||
|
||||
public boolean hasPermission(Player member) {
|
||||
if (SteamwarUser.get(member.getUniqueId()).getId() == BauServer.getInstance().getOwnerID()) {
|
||||
SteamwarUser steamwarUser = SteamwarUser.get(member.getUniqueId());
|
||||
BauServer server = BauServer.getInstance();
|
||||
if (!server.isTeamWorld() && steamwarUser.getId() == server.getOwnerID()) {
|
||||
return this != SPECTATOR;
|
||||
}
|
||||
BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getOwner(), member.getUniqueId());
|
||||
if (server.isTeamWorld() && steamwarUser.getTeam() == server.getTeamID()) {
|
||||
return this != SPECTATOR;
|
||||
}
|
||||
BauweltMember bauweltMember = BauweltMember.getBauMember(server.getWorldID(), steamwarUser.getId());
|
||||
if (bauweltMember == null) return this == SPECTATOR;
|
||||
return permissionPredicate.test(bauweltMember);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,16 +34,41 @@ public class BauServer {
|
||||
}
|
||||
|
||||
private Integer owner;
|
||||
private UUID world;
|
||||
private Integer team;
|
||||
|
||||
public UUID getOwner() {
|
||||
return SteamwarUser.byId(getOwnerID()).getUUID();
|
||||
Integer ownerId = getOwnerID();
|
||||
return ownerId == null ? null : SteamwarUser.byId(ownerId).getUUID();
|
||||
}
|
||||
|
||||
public int getOwnerID() {
|
||||
public Integer getOwnerID() {
|
||||
//Lazy loading to improve startup time of the server in 1.15
|
||||
if (owner == null) {
|
||||
owner = BauServerInfo.getOwnerId();
|
||||
}
|
||||
return owner;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasOwner() {
|
||||
return getOwnerID() != null;
|
||||
}
|
||||
|
||||
public UUID getWorldID() {
|
||||
if (world == null) {
|
||||
world = BauServerInfo.getWorldId();
|
||||
}
|
||||
return world;
|
||||
}
|
||||
|
||||
public Integer getTeamID() {
|
||||
if (team == null) {
|
||||
team = BauServerInfo.getTeamId();
|
||||
}
|
||||
return team;
|
||||
}
|
||||
|
||||
public boolean isTeamWorld() {
|
||||
return getTeamID() != null;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ public class BauInfoBauGuiItem extends BauGuiItem {
|
||||
@Override
|
||||
public ItemStack getItem(Player player) {
|
||||
SWItem itemStack;
|
||||
if (!player.getName().endsWith("⍇")) {
|
||||
if (!player.getName().endsWith("⍇") && !BauServer.getInstance().isTeamWorld()) {
|
||||
itemStack = SWItem.getPlayerSkull(SteamwarUser.get(BauServer.getInstance().getOwner()).getUserName());
|
||||
} else {
|
||||
itemStack = new SWItem(Material.PLAYER_HEAD, "");
|
||||
|
||||
@@ -47,7 +47,9 @@ public class InfoCommand extends SWCommand {
|
||||
|
||||
@Register(description = "BAU_INFO_COMMAND_HELP")
|
||||
public void genericCommand(Player p) {
|
||||
BauSystem.MESSAGE.send("BAU_INFO_COMMAND_OWNER", p, SteamwarUser.byId(bauServer.getOwnerID()).getUserName());
|
||||
if (!bauServer.isTeamWorld()) {
|
||||
BauSystem.MESSAGE.send("BAU_INFO_COMMAND_OWNER", p, SteamwarUser.byId(bauServer.getOwnerID()).getUserName());
|
||||
}
|
||||
Region region = Region.getRegion(p.getLocation());
|
||||
for (Flag flag : Flag.getFlags()) {
|
||||
if (!region.getRegionData().has(flag).isApplicable()) continue;
|
||||
@@ -58,7 +60,7 @@ public class InfoCommand extends SWCommand {
|
||||
}
|
||||
|
||||
if (Permission.BUILD.hasPermission(p)) {
|
||||
List<BauweltMember> members = BauweltMember.getMembers(bauServer.getOwnerID());
|
||||
List<BauweltMember> members = BauweltMember.getWorldMembers(bauServer.getWorldID());
|
||||
Map<Permission, List<BauweltMember>> memberByPermission = new HashMap<>();
|
||||
members.forEach(member -> {
|
||||
if (Permission.SUPERVISOR.hasPermission(member)) {
|
||||
|
||||
+15
-14
@@ -21,9 +21,7 @@ package de.steamwar.bausystem.features.design.endstone;
|
||||
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.bausystem.region.Region;
|
||||
import de.steamwar.entity.REntity;
|
||||
import de.steamwar.entity.REntityServer;
|
||||
import de.steamwar.entity.RFallingBlockEntity;
|
||||
import de.steamwar.entity.*;
|
||||
import net.md_5.bungee.api.ChatMessageType;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
@@ -59,15 +57,15 @@ public class DesignEndStone {
|
||||
.filter(material -> material.getBlastResistance() > region.getGameModeConfig().Schematic.MaxDesignBlastResistance)
|
||||
.collect(Collectors.toSet());
|
||||
calculateFromBottom = region.getGameModeConfig().Arena.NoFloor;
|
||||
}
|
||||
|
||||
entityServer.setCallback((player, rEntity, entityAction) -> {
|
||||
if (entityAction != REntityServer.EntityAction.ATTACK) return;
|
||||
Location location = new Location(WORLD, rEntity.getX(), rEntity.getY(), rEntity.getZ());
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||
location.getBlock().breakNaturally();
|
||||
calc();
|
||||
}, 1);
|
||||
});
|
||||
private void interact(Player player, RInteraction entity, REntityAction action) {
|
||||
if (action != REntityAction.ATTACK) return;
|
||||
Location location = new Location(WORLD, entity.getX(), entity.getY(), entity.getZ());
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||
location.getBlock().breakNaturally();
|
||||
calc();
|
||||
}, 1);
|
||||
}
|
||||
|
||||
public void calc() {
|
||||
@@ -110,12 +108,15 @@ public class DesignEndStone {
|
||||
Material material = WORLD.getBlockAt(cx, cy, cz).getType();
|
||||
|
||||
if (material != Material.WATER && material != Material.LAVA && limited.contains(material)) {
|
||||
Location location = new Location(WORLD, cx + 0.5, cy, cz + 0.5);
|
||||
Location location = new Location(WORLD, cx, cy, cz);
|
||||
if (!locations.add(location)) break;
|
||||
RFallingBlockEntity entity = new RFallingBlockEntity(entityServer, location, Material.RED_STAINED_GLASS);
|
||||
entity.setNoGravity(true);
|
||||
RBlockDisplay entity = new RBlockDisplay(entityServer, location);
|
||||
entity.setBlock(Material.RED_STAINED_GLASS.createBlockData());
|
||||
entity.setGlowing(true);
|
||||
entities.add(entity);
|
||||
RInteraction interaction = new RInteraction(entityServer, location);
|
||||
interaction.setCallback(this::interact);
|
||||
entities.add(interaction);
|
||||
break;
|
||||
} else if (!material.isAir() && material != Material.WATER && material != Material.LAVA) {
|
||||
break;
|
||||
|
||||
+10
-9
@@ -26,8 +26,9 @@ import de.steamwar.bausystem.features.autostart.AutostartListener;
|
||||
import de.steamwar.bausystem.features.detonator.storage.DetonatorStorage;
|
||||
import de.steamwar.bausystem.features.detonator.storage.ItemStorage;
|
||||
import de.steamwar.core.SWPlayer;
|
||||
import de.steamwar.entity.RBlockDisplay;
|
||||
import de.steamwar.entity.REntityServer;
|
||||
import de.steamwar.entity.RFallingBlockEntity;
|
||||
import de.steamwar.entity.RInteraction;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.UtilityClass;
|
||||
@@ -58,10 +59,6 @@ public class Detonator {
|
||||
@Override
|
||||
public void onMount(SWPlayer player) {
|
||||
entities.addPlayer(player.getPlayer());
|
||||
entities.setCallback((player1, entity, action) -> {
|
||||
Vector vector = new Vector(entity.getX(), entity.getY(), entity.getZ());
|
||||
DetonatorListener.addLocationToDetonator(vector.toLocation(player.getWorld()).getBlock().getLocation(), player1);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,8 +67,6 @@ public class Detonator {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Vector HALF = new Vector(0.5, 0, 0.5);
|
||||
|
||||
public static boolean isDetonator(ItemStack itemStack) {
|
||||
return ItemStorage.isDetonator(itemStack);
|
||||
}
|
||||
@@ -79,8 +74,14 @@ public class Detonator {
|
||||
public static void showDetonator(Player p, List<Location> locs) {
|
||||
DetonatorComponent detonatorComponent = SWPlayer.of(p).getComponentOrDefault(DetonatorComponent.class, DetonatorComponent::new);
|
||||
locs.forEach(location -> {
|
||||
RFallingBlockEntity entity = new RFallingBlockEntity(detonatorComponent.entities, location.clone().add(HALF), Material.RED_STAINED_GLASS);
|
||||
entity.setNoGravity(true);
|
||||
RBlockDisplay blockDisplay = new RBlockDisplay(detonatorComponent.entities, location);
|
||||
blockDisplay.setBlock(Material.RED_STAINED_GLASS.createBlockData());
|
||||
|
||||
RInteraction interaction = new RInteraction(detonatorComponent.entities, location);
|
||||
interaction.setCallback((player, entity, action) -> {
|
||||
Vector vector = new Vector(entity.getX(), entity.getY(), entity.getZ());
|
||||
DetonatorListener.addLocationToDetonator(vector.toLocation(player.getWorld()).getBlock().getLocation(), player);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -24,9 +24,9 @@ import de.steamwar.bausystem.region.Point;
|
||||
import de.steamwar.bausystem.region.Region;
|
||||
import de.steamwar.bausystem.utils.bossbar.BauSystemBossbar;
|
||||
import de.steamwar.bausystem.utils.bossbar.BossBarService;
|
||||
import de.steamwar.entity.RBlockDisplay;
|
||||
import de.steamwar.entity.REntity;
|
||||
import de.steamwar.entity.REntityServer;
|
||||
import de.steamwar.entity.RFallingBlockEntity;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
@@ -290,8 +290,8 @@ public class KillcheckerVisualizer {
|
||||
}
|
||||
rEntities.get(point).die();
|
||||
}
|
||||
RFallingBlockEntity entity = new RFallingBlockEntity(outlinePoints.contains(point) ? outline : inner, point.toLocation(WORLD, 0.5, 0, 0.5), MATERIALS[Math.min(count - 1, MATERIALS.length) - 1]);
|
||||
entity.setNoGravity(true);
|
||||
RBlockDisplay entity = new RBlockDisplay(outlinePoints.contains(point) ? outline : inner, point.toLocation(WORLD, 0.5, 0, 0.5));
|
||||
entity.setBlock(MATERIALS[Math.min(count - 1, MATERIALS.length) - 1].createBlockData());
|
||||
rEntities.put(point, entity);
|
||||
if (outlinePoints.contains(point)) outlinePointsCache.add(point);
|
||||
killCount.put(point, count);
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
package de.steamwar.bausystem.features.region;
|
||||
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.bausystem.Permission;
|
||||
import de.steamwar.bausystem.config.BauServer;
|
||||
import de.steamwar.bausystem.region.Region;
|
||||
import de.steamwar.bausystem.region.RegionUtils;
|
||||
@@ -83,6 +84,9 @@ public class ColorCommand extends SWCommand {
|
||||
@ClassValidator(value = Player.class, local = true)
|
||||
public TypeValidator<Player> validator() {
|
||||
return (commandSender, player, messageSender) -> {
|
||||
if (bauServer.isTeamWorld()) {
|
||||
return !messageSender.send(!Permission.SUPERVISOR.hasPermission(player), "NO_PERMISSION");
|
||||
}
|
||||
return !messageSender.send(!bauServer.getOwner().equals(player.getUniqueId()), "NO_PERMISSION");
|
||||
};
|
||||
}
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ public class ResetCommand extends SWCommand {
|
||||
Region region = regionCheck(p);
|
||||
if (region == null) return;
|
||||
|
||||
if (!p.getUniqueId().equals(bauServer.getOwner())) {
|
||||
if (bauServer.hasOwner() && !p.getUniqueId().equals(bauServer.getOwner())) {
|
||||
if (Punishment.isPunished(SteamwarUser.get(bauServer.getOwner()), Punishment.PunishmentType.NoSchemReceiving, punishment -> BauSystem.MESSAGE.send("REGION_TB_NO_SCHEMRECEIVING", p, punishment.getEndTime()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
+1
-1
@@ -91,7 +91,7 @@ public class TestblockCommand extends SWCommand {
|
||||
}
|
||||
}
|
||||
|
||||
if (!p.getUniqueId().equals(bauServer.getOwner())) {
|
||||
if (bauServer.hasOwner() && !p.getUniqueId().equals(bauServer.getOwner())) {
|
||||
if (Punishment.isPunished(SteamwarUser.get(bauServer.getOwner()), Punishment.PunishmentType.NoSchemReceiving, punishment -> BauSystem.MESSAGE.send("REGION_TB_NO_SCHEMRECEIVING", p, punishment.getEndTime()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
+1
@@ -38,6 +38,7 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import de.steamwar.techhider.legacy.TechHider;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
|
||||
package de.steamwar.bausystem.features.tpslimit;
|
||||
|
||||
import com.destroystokyo.paper.event.server.ServerTickEndEvent;
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.bausystem.Permission;
|
||||
import de.steamwar.bausystem.SWUtils;
|
||||
import de.steamwar.bausystem.linkage.BauGuiItem;
|
||||
import de.steamwar.bausystem.region.Region;
|
||||
import de.steamwar.bausystem.utils.ScoreboardElement;
|
||||
import de.steamwar.bausystem.utils.TickEndEvent;
|
||||
import de.steamwar.bausystem.utils.TickManager;
|
||||
import de.steamwar.bausystem.utils.bossbar.BauSystemBossbar;
|
||||
import de.steamwar.bausystem.utils.bossbar.BossBarService;
|
||||
@@ -72,7 +72,7 @@ public class TPSSystem implements Listener {
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onTickEnd(TickEndEvent event) {
|
||||
public void onTickEnd(ServerTickEndEvent event) {
|
||||
bossbar();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import de.steamwar.bausystem.features.tracer.rendering.TraceEntity;
|
||||
import de.steamwar.bausystem.features.tracer.rendering.ViewFlag;
|
||||
import de.steamwar.bausystem.region.Region;
|
||||
import de.steamwar.entity.REntity;
|
||||
import de.steamwar.entity.REntityAction;
|
||||
import de.steamwar.entity.REntityServer;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@@ -143,14 +144,6 @@ public class Trace {
|
||||
} else {
|
||||
entityServer = new REntityServer();
|
||||
entityServer.addPlayer(player);
|
||||
entityServer.setCallback((p, rEntity, entityAction) -> {
|
||||
if (entityAction != REntityServer.EntityAction.INTERACT) {
|
||||
return;
|
||||
}
|
||||
if (rEntity instanceof TraceEntity) {
|
||||
((TraceEntity) rEntity).printIntoChat(p);
|
||||
}
|
||||
});
|
||||
entityServerMap.put(player, entityServer);
|
||||
}
|
||||
render(getRecords(), entityServer, playerTraceShowData);
|
||||
@@ -167,14 +160,6 @@ public class Trace {
|
||||
REntityServer entityServer = entityServerMap.computeIfAbsent(player, k -> {
|
||||
REntityServer newEntityServer = new REntityServer();
|
||||
newEntityServer.addPlayer(k);
|
||||
newEntityServer.setCallback((p, rEntity, entityAction) -> {
|
||||
if (entityAction != REntityServer.EntityAction.INTERACT) {
|
||||
return;
|
||||
}
|
||||
if (rEntity instanceof TraceEntity) {
|
||||
((TraceEntity) rEntity).printIntoChat(p);
|
||||
}
|
||||
});
|
||||
return newEntityServer;
|
||||
});
|
||||
|
||||
|
||||
+36
-4
@@ -24,13 +24,18 @@ import de.steamwar.bausystem.configplayer.Config;
|
||||
import de.steamwar.bausystem.features.tracer.TNTPoint;
|
||||
import de.steamwar.bausystem.features.tracer.Trace;
|
||||
import de.steamwar.bausystem.features.tracer.TraceManager;
|
||||
import de.steamwar.entity.RBlockDisplay;
|
||||
import de.steamwar.entity.REntityAction;
|
||||
import de.steamwar.entity.REntityServer;
|
||||
import de.steamwar.entity.RFallingBlockEntity;
|
||||
import de.steamwar.entity.RInteraction;
|
||||
import lombok.Getter;
|
||||
import net.md_5.bungee.api.chat.ClickEvent;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Transformation;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3f;
|
||||
import yapion.hierarchy.types.YAPIONValue;
|
||||
|
||||
import java.util.List;
|
||||
@@ -41,7 +46,16 @@ import static de.steamwar.bausystem.features.util.TNTClickListener.TNT_CLICK_DET
|
||||
/**
|
||||
* Wrapper for the rendering of a record bundle
|
||||
*/
|
||||
public class TraceEntity extends RFallingBlockEntity {
|
||||
public class TraceEntity extends RBlockDisplay {
|
||||
|
||||
private static final float TNT_VISUAL_SCALE = 0.98F;
|
||||
private static final float TNT_VISUAL_OFFSET = -TNT_VISUAL_SCALE / 2.0F;
|
||||
private static final Transformation TNT_VISUAL_TRANSFORM = new Transformation(
|
||||
new Vector3f(TNT_VISUAL_OFFSET, 0.0F, TNT_VISUAL_OFFSET),
|
||||
new Quaternionf(0, 0, 0, 1),
|
||||
new Vector3f(TNT_VISUAL_SCALE, TNT_VISUAL_SCALE, TNT_VISUAL_SCALE),
|
||||
new Quaternionf(0, 0, 0, 1)
|
||||
);
|
||||
|
||||
/**
|
||||
* The records represented by this REntity
|
||||
@@ -55,13 +69,31 @@ public class TraceEntity extends RFallingBlockEntity {
|
||||
private final String uniqueTntIdsString;
|
||||
|
||||
private final Trace trace;
|
||||
private final RInteraction hitbox;
|
||||
|
||||
public TraceEntity(REntityServer server, Location location, boolean isExplosion, List<TNTPoint> records, Trace trace) {
|
||||
super(server, location, isExplosion ? Material.RED_STAINED_GLASS : Material.TNT);
|
||||
super(server, location);
|
||||
Material material = isExplosion ? Material.RED_STAINED_GLASS : Material.TNT;
|
||||
this.records = records;
|
||||
this.trace = trace;
|
||||
uniqueTntIdsString = records.stream().map(TNTPoint::getTntId).distinct().map(Object::toString).collect(Collectors.joining(" "));
|
||||
setNoGravity(true);
|
||||
hitbox = new RInteraction(server, location);
|
||||
hitbox.setInteractionHeight(TNT_VISUAL_SCALE);
|
||||
hitbox.setInteractionWidth(TNT_VISUAL_SCALE);
|
||||
hitbox.setCallback((player, action) -> {
|
||||
if (action == REntityAction.INTERACT) {
|
||||
printIntoChat(player);
|
||||
}
|
||||
});
|
||||
setTransform(TNT_VISUAL_TRANSFORM);
|
||||
setBlock(material.createBlockData());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void die() {
|
||||
hitbox.die();
|
||||
super.die();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-5
@@ -20,8 +20,8 @@
|
||||
package de.steamwar.bausystem.features.tracer.rendering;
|
||||
|
||||
import de.steamwar.bausystem.features.tracer.TNTPoint;
|
||||
import de.steamwar.entity.RBlockDisplay;
|
||||
import de.steamwar.entity.REntityServer;
|
||||
import de.steamwar.entity.RFallingBlockEntity;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.util.Vector;
|
||||
@@ -129,8 +129,8 @@ public abstract class ViewFlag {
|
||||
|
||||
Location yLocation = previous.getLocation().clone().add(0, delta.getY(), 0);
|
||||
if (yLocation.distanceSquared(representative.getLocation()) >= 1.0 / 256.0 && yLocation.distanceSquared(previous.getLocation()) >= 1.0 / 256.0) {
|
||||
RFallingBlockEntity y = new RFallingBlockEntity(server, yLocation, Material.WHITE_STAINED_GLASS);
|
||||
y.setNoGravity(true);
|
||||
RBlockDisplay y = new RBlockDisplay(server, yLocation);
|
||||
y.setBlock(Material.WHITE_STAINED_GLASS.createBlockData());
|
||||
}
|
||||
|
||||
Location secoundLocation;
|
||||
@@ -141,8 +141,8 @@ public abstract class ViewFlag {
|
||||
}
|
||||
|
||||
if (secoundLocation.distanceSquared(representative.getLocation()) >= 1.0 / 256.0 && secoundLocation.distanceSquared(previous.getLocation()) >= 1.0 / 256.0) {
|
||||
RFallingBlockEntity second = new RFallingBlockEntity(server, secoundLocation, Material.WHITE_STAINED_GLASS);
|
||||
second.setNoGravity(true);
|
||||
RBlockDisplay second = new RBlockDisplay(server, secoundLocation);
|
||||
second.setBlock(Material.WHITE_STAINED_GLASS.createBlockData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -20,6 +20,7 @@
|
||||
package de.steamwar.bausystem.features.world;
|
||||
|
||||
import de.steamwar.bausystem.BauSystem;
|
||||
import de.steamwar.bausystem.Permission;
|
||||
import de.steamwar.bausystem.config.BauServer;
|
||||
import de.steamwar.core.CRIUWakeupEvent;
|
||||
import de.steamwar.linkage.Linked;
|
||||
@@ -36,12 +37,13 @@ public class AntiBauAddMemberFix implements Listener {
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
if (BauSystem.DEV_SERVER) return;
|
||||
if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) {
|
||||
if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
|
||||
return;
|
||||
}
|
||||
if (BauweltMember.getBauMember(BauServer.getInstance().getOwner(), event.getPlayer().getUniqueId()) == null) {
|
||||
if (BauweltMember.getBauMember(BauServer.getInstance().getWorldID(), event.getPlayer().getUniqueId()) == null) {
|
||||
event.getPlayer().kickPlayer("");
|
||||
throw new SecurityException("The player " + event.getPlayer().getName() + " joined on the server of " + SteamwarUser.byId(BauServer.getInstance().getOwnerID()).getUserName() + " without being added!");
|
||||
String owner = BauServer.getInstance().isTeamWorld() ? "team " + BauServer.getInstance().getTeamID() : SteamwarUser.byId(BauServer.getInstance().getOwnerID()).getUserName();
|
||||
throw new SecurityException("The player " + event.getPlayer().getName() + " joined on the server of " + owner + " without being added!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -33,7 +33,7 @@ public class AxiomPermissionCheck implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onAxiomHandshake(AxiomHandshakeEvent event) {
|
||||
if (Permission.SUPERVISOR.hasPermission(event.getPlayer()) || BauServer.getInstance().getOwner().equals(event.getPlayer().getUniqueId())) {
|
||||
if (Permission.SUPERVISOR.hasPermission(event.getPlayer()) || (BauServer.getInstance().hasOwner() && BauServer.getInstance().getOwner().equals(event.getPlayer().getUniqueId()))) {
|
||||
event.setMaxBufferSize(Short.MAX_VALUE);
|
||||
return;
|
||||
}
|
||||
|
||||
+5
-4
@@ -28,7 +28,7 @@ import de.steamwar.data.BauLockState;
|
||||
import de.steamwar.linkage.Linked;
|
||||
import de.steamwar.network.packets.PacketHandler;
|
||||
import de.steamwar.network.packets.server.BaulockUpdatePacket;
|
||||
import de.steamwar.sql.UserConfig;
|
||||
import de.steamwar.sql.SteamwarWorld;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
@@ -37,13 +37,13 @@ import org.bukkit.event.Listener;
|
||||
@Linked
|
||||
public class BauLockStateScoreboard extends PacketHandler implements ScoreboardElement, Listener {
|
||||
|
||||
private static final String BAU_LOCK_CONFIG_NAME = "baulockstate";
|
||||
|
||||
@Getter
|
||||
private BauLockState lockState = loadLockState();
|
||||
|
||||
private BauLockState loadLockState() {
|
||||
String state = UserConfig.getConfig(BauServer.getInstance().getOwner(), BAU_LOCK_CONFIG_NAME);
|
||||
if (BauServer.getInstance().isTeamWorld()) return BauLockState.OPEN;
|
||||
SteamwarWorld world = SteamwarWorld.getWorld(BauServer.getInstance().getWorldID());
|
||||
String state = world == null ? null : world.getLockState();
|
||||
return state == null ? BauLockState.OPEN : BauLockState.valueOf(state);
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ public class BauLockStateScoreboard extends PacketHandler implements ScoreboardE
|
||||
|
||||
@Override
|
||||
public String get(Region region, Player p) {
|
||||
if (BauServer.getInstance().isTeamWorld()) return null;
|
||||
if (!BauServer.getInstance().getOwner().equals(p.getUniqueId())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+10
@@ -28,6 +28,7 @@ import de.steamwar.bausystem.utils.bossbar.BossBarService;
|
||||
import de.steamwar.linkage.Linked;
|
||||
import de.steamwar.network.packets.PacketHandler;
|
||||
import de.steamwar.network.packets.server.BaumemberUpdatePacket;
|
||||
import io.papermc.paper.event.player.PlayerPickBlockEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.boss.BarColor;
|
||||
import org.bukkit.boss.BarStyle;
|
||||
@@ -35,6 +36,8 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||
import org.bukkit.event.inventory.ClickType;
|
||||
import org.bukkit.event.inventory.InventoryClickEvent;
|
||||
import org.bukkit.event.player.PlayerItemConsumeEvent;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
@@ -127,4 +130,11 @@ public class BauMemberUpdate extends PacketHandler implements Listener {
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerClick(PlayerPickBlockEvent event) {
|
||||
if(SPECTATORS.contains(event.getPlayer())) {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -42,7 +42,11 @@ public class KickallCommand extends SWCommand {
|
||||
if (!Permission.OWNER.hasPermission(player)) return;
|
||||
|
||||
Bukkit.getOnlinePlayers().forEach(p -> {
|
||||
if (!bauServer.getOwner().equals(p.getUniqueId())) p.kickPlayer("");
|
||||
if (bauServer.isTeamWorld()) {
|
||||
if (!Permission.SUPERVISOR.hasPermission(p)) p.kickPlayer("");
|
||||
} else if (!bauServer.getOwner().equals(p.getUniqueId())) {
|
||||
p.kickPlayer("");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -39,6 +39,7 @@ import org.bukkit.event.block.BlockPlaceEvent;
|
||||
import org.bukkit.event.entity.EntityPickupItemEvent;
|
||||
import org.bukkit.event.player.*;
|
||||
import org.bukkit.util.Vector;
|
||||
import de.steamwar.techhider.legacy.TechHider;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -142,17 +143,14 @@ public class SpectatorListener implements Listener {
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
enableOrDisableTechhider();
|
||||
if (BauSystem.DEV_SERVER) return;
|
||||
if (event.getPlayer().getUniqueId().equals(BauServer.getInstance().getOwner())) {
|
||||
if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
|
||||
return;
|
||||
}
|
||||
BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getOwner(), event.getPlayer().getUniqueId());
|
||||
BauweltMember bauweltMember = BauweltMember.getBauMember(BauServer.getInstance().getWorldID(), event.getPlayer().getUniqueId());
|
||||
if (bauweltMember == null) {
|
||||
event.getPlayer().kickPlayer("");
|
||||
return;
|
||||
}
|
||||
if (Permission.SUPERVISOR.hasPermission(event.getPlayer())) {
|
||||
return;
|
||||
}
|
||||
if (!anySupervisorOnline(null)) {
|
||||
Bukkit.getScheduler().runTaskLater(BauSystem.getInstance(), () -> {
|
||||
event.getPlayer().kickPlayer("");
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import de.steamwar.techhider.legacy.TechHider;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
+11
-1
@@ -11,13 +11,23 @@ import de.steamwar.commands.profiler.ProfilerCommand
|
||||
import de.steamwar.commands.user.UserCommand
|
||||
import de.steamwar.commands.user.UserInfoCommand
|
||||
import de.steamwar.commands.user.UserSearchCommand
|
||||
import de.steamwar.commands.world.MigrationCommand
|
||||
import de.steamwar.commands.world.SaveToStorageCommand
|
||||
import de.steamwar.commands.world.TemplateCommand
|
||||
import de.steamwar.commands.world.TemplateCreateCommand
|
||||
import de.steamwar.commands.world.WorldCommand
|
||||
|
||||
fun main(args: Array<String>) =
|
||||
SteamWar()
|
||||
.subcommands(
|
||||
DatabaseCommand().subcommands(InfoCommand(), ResetCommand()),
|
||||
UserCommand().subcommands(UserInfoCommand(), UserSearchCommand()),
|
||||
WorldCommand().subcommands(
|
||||
MigrationCommand(),
|
||||
SaveToStorageCommand(),
|
||||
TemplateCommand().subcommands(TemplateCreateCommand())
|
||||
),
|
||||
DevCommand(),
|
||||
ProfilerCommand()
|
||||
)
|
||||
.main(args)
|
||||
.main(args)
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
package de.steamwar.commands.world
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import de.steamwar.db.Database
|
||||
import de.steamwar.sql.SteamwarUser
|
||||
import de.steamwar.sql.SteamwarWorld
|
||||
import de.steamwar.sql.UserConfig
|
||||
import org.jetbrains.exposed.v1.jdbc.transactions.transaction
|
||||
import java.io.File
|
||||
import java.sql.SQLException
|
||||
import java.util.UUID
|
||||
|
||||
class MigrationCommand : CliktCommand(name = "migrate") {
|
||||
private val userWorldPattern = Regex("""userworlds(\d+)""")
|
||||
private val builderWorldPattern = Regex("""builder(\d+)""")
|
||||
private val trailingVersionPattern = Regex("""(\d+)$""")
|
||||
|
||||
override fun run() {
|
||||
Database.ensureConnected()
|
||||
|
||||
val bau = migrateBauWorlds()
|
||||
val builder = migrateBuilderWorlds()
|
||||
val arena = migrateArenaWorlds()
|
||||
migrateBauLockConfig()
|
||||
migrateBauweltMembers()
|
||||
|
||||
echo("Imported ${bau.imported} build worlds, skipped ${bau.skipped}, missing users ${bau.missingOwner}.")
|
||||
echo("Imported ${builder.imported} builder worlds, skipped ${builder.skipped}.")
|
||||
echo("Imported ${arena.imported} arena worlds, skipped ${arena.skipped}.")
|
||||
echo("World migration completed.")
|
||||
}
|
||||
|
||||
private fun migrateBauWorlds(): MigrationStats {
|
||||
val stats = MigrationStats()
|
||||
val worldsRoot = File("/worlds")
|
||||
worldsRoot.listFiles { file -> file.isDirectory && userWorldPattern.matches(file.name) }
|
||||
?.forEach { versionFolder ->
|
||||
val version = userWorldPattern.matchEntire(versionFolder.name)!!.groupValues[1].toInt()
|
||||
versionFolder.listFiles { file -> file.isDirectory && isWorldDirectory(file) }
|
||||
?.sortedBy { it.name }
|
||||
?.forEach { legacyWorld ->
|
||||
val owner = legacyWorld.ownerUser()
|
||||
if (owner == null) {
|
||||
stats.missingOwner++
|
||||
echo("Skipped build world ${legacyWorld.path}: no matching user.")
|
||||
return@forEach
|
||||
}
|
||||
|
||||
val lockState = UserConfig.getConfig(owner.id.value, BAU_LOCK_CONFIG_NAME)
|
||||
val world = SteamwarWorld.getOrCreateBauWorld(owner, owner.userName, version, legacyWorld)
|
||||
if (world.lockState == null && lockState != null) {
|
||||
world.changeLockState(lockState)
|
||||
}
|
||||
stats.imported++
|
||||
}
|
||||
} ?: run {
|
||||
stats.skipped++
|
||||
echo("Skipped build worlds: /worlds does not exist.")
|
||||
}
|
||||
return stats
|
||||
}
|
||||
|
||||
private fun migrateBuilderWorlds(): MigrationStats {
|
||||
val stats = MigrationStats()
|
||||
val worldsRoot = File("/worlds")
|
||||
worldsRoot.listFiles { file -> file.isDirectory && builderWorldPattern.matches(file.name) }
|
||||
?.forEach { versionFolder ->
|
||||
val version = builderWorldPattern.matchEntire(versionFolder.name)!!.groupValues[1].toInt()
|
||||
versionFolder.listFiles { file -> file.isDirectory && isWorldDirectory(file) }
|
||||
?.sortedBy { it.name }
|
||||
?.forEach { legacyWorld ->
|
||||
SteamwarWorld.getOrCreateBuilderWorld(legacyWorld.name, version, legacyWorld)
|
||||
stats.imported++
|
||||
}
|
||||
} ?: run {
|
||||
stats.skipped++
|
||||
echo("Skipped builder worlds: /worlds does not exist.")
|
||||
}
|
||||
return stats
|
||||
}
|
||||
|
||||
private fun migrateArenaWorlds(): MigrationStats {
|
||||
val stats = MigrationStats()
|
||||
val configs = parseGameModeConfigs(File("/configs/GameModes"))
|
||||
if (configs.isEmpty()) {
|
||||
stats.skipped++
|
||||
echo("Skipped arena worlds: no game mode configs found in /configs/GameModes.")
|
||||
return stats
|
||||
}
|
||||
|
||||
for (config in configs) {
|
||||
val version = trailingVersionPattern.find(config.serverFolder)?.value?.toIntOrNull()
|
||||
if (version == null) {
|
||||
stats.skipped++
|
||||
echo("Skipped arena mode ${config.modeName}: server folder '${config.serverFolder}' has no version suffix.")
|
||||
continue
|
||||
}
|
||||
|
||||
val arenaRoot = File("/servers/${config.serverFolder}/arenas")
|
||||
for (map in config.maps) {
|
||||
val legacyWorld = File(arenaRoot, map)
|
||||
if (!isWorldDirectory(legacyWorld)) {
|
||||
stats.skipped++
|
||||
continue
|
||||
}
|
||||
|
||||
SteamwarWorld.getOrCreateArenaWorld(config.modeName, map, version, legacyWorld)
|
||||
stats.imported++
|
||||
}
|
||||
}
|
||||
|
||||
return stats
|
||||
}
|
||||
|
||||
private fun migrateBauLockConfig() {
|
||||
transaction(Database.db) {
|
||||
exec(
|
||||
"""
|
||||
UPDATE `world` w
|
||||
JOIN `UserConfig` uc ON uc.`User` = w.`Owner` AND uc.`Config` = 'baulockstate'
|
||||
SET w.`LockState` = uc.`Value`
|
||||
WHERE w.`Owner` IS NOT NULL AND w.`LockState` IS NULL
|
||||
""".trimIndent()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun migrateBauweltMembers() {
|
||||
if (!columnExists("BauweltMember", "WorldID") || !columnExists("BauweltMember", "BauweltID")) {
|
||||
echo("Skipped BauweltMember data migration: expected WorldID and BauweltID columns are not both present.")
|
||||
return
|
||||
}
|
||||
|
||||
execIgnore(
|
||||
"""
|
||||
INSERT IGNORE INTO `BauweltMember` (`WorldID`, `MemberID`, `Build`, `WorldEdit`, `World`)
|
||||
SELECT w.`id`, bm.`MemberID`, bm.`Build`, bm.`WorldEdit`, bm.`World`
|
||||
FROM `BauweltMember` bm
|
||||
JOIN `world` w ON w.`Owner` = bm.`BauweltID` AND w.`Deleted` = 0
|
||||
""".trimIndent(),
|
||||
"Skipped BauweltMember data migration"
|
||||
)
|
||||
}
|
||||
|
||||
private fun parseGameModeConfigs(configRoot: File): List<ArenaMigrationConfig> {
|
||||
if (!configRoot.isDirectory) return emptyList()
|
||||
|
||||
return configRoot.listFiles { file -> file.isFile && file.extension == "yml" && !file.name.endsWith(".kits.yml") }
|
||||
?.mapNotNull { file ->
|
||||
val server = parseServerBlock(file)
|
||||
val folder = server.folder ?: return@mapNotNull null
|
||||
ArenaMigrationConfig(file.nameWithoutExtension, folder, server.maps)
|
||||
}
|
||||
?: emptyList()
|
||||
}
|
||||
|
||||
private fun parseServerBlock(file: File): ParsedServerBlock {
|
||||
var inServer = false
|
||||
var inMaps = false
|
||||
var folder: String? = null
|
||||
val maps = mutableListOf<String>()
|
||||
|
||||
file.readLines().forEach { line ->
|
||||
val trimmed = line.trim()
|
||||
if (trimmed.isEmpty() || trimmed.startsWith("#")) return@forEach
|
||||
|
||||
val indent = line.indexOfFirst { !it.isWhitespace() }.let { if (it == -1) 0 else it }
|
||||
if (indent == 0) {
|
||||
inServer = trimmed == "Server:"
|
||||
inMaps = false
|
||||
return@forEach
|
||||
}
|
||||
|
||||
if (!inServer) return@forEach
|
||||
|
||||
when {
|
||||
indent <= 2 && trimmed.startsWith("Folder:") -> folder = trimmed.substringAfter(":").cleanYamlValue()
|
||||
indent <= 2 && trimmed.startsWith("Maps:") -> {
|
||||
val value = trimmed.substringAfter(":").trim()
|
||||
inMaps = true
|
||||
if (value.startsWith("[") && value.endsWith("]")) {
|
||||
maps += value.removePrefix("[").removeSuffix("]")
|
||||
.split(",")
|
||||
.map { it.cleanYamlValue() }
|
||||
.filter { it.isNotEmpty() }
|
||||
inMaps = false
|
||||
}
|
||||
}
|
||||
inMaps && trimmed.startsWith("-") -> maps += trimmed.removePrefix("-").cleanYamlValue()
|
||||
inMaps && indent <= 2 -> inMaps = false
|
||||
}
|
||||
}
|
||||
|
||||
return ParsedServerBlock(folder, maps)
|
||||
}
|
||||
|
||||
private fun File.ownerUser(): SteamwarUser? {
|
||||
name.toIntOrNull()?.let { return SteamwarUser.byId(it) }
|
||||
return runCatching { SteamwarUser.get(UUID.fromString(name)) }.getOrNull()
|
||||
}
|
||||
|
||||
private fun isWorldDirectory(file: File): Boolean =
|
||||
file.isDirectory && (File(file, "level.dat").isFile || File(file, "region").isDirectory)
|
||||
|
||||
private fun String.cleanYamlValue(): String =
|
||||
trim().trim('"').trim('\'')
|
||||
|
||||
private fun columnExists(table: String, column: String): Boolean =
|
||||
transaction(Database.db) {
|
||||
exec("SHOW COLUMNS FROM `$table` LIKE '$column'") { result -> result.next() } ?: false
|
||||
}
|
||||
|
||||
private fun execIgnore(sql: String, prefix: String) {
|
||||
try {
|
||||
transaction(Database.db) {
|
||||
exec(sql)
|
||||
}
|
||||
} catch (e: SQLException) {
|
||||
echo("$prefix: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
private data class MigrationStats(
|
||||
var imported: Int = 0,
|
||||
var skipped: Int = 0,
|
||||
var missingOwner: Int = 0,
|
||||
)
|
||||
|
||||
private data class ArenaMigrationConfig(
|
||||
val modeName: String,
|
||||
val serverFolder: String,
|
||||
val maps: List<String>,
|
||||
)
|
||||
|
||||
private data class ParsedServerBlock(
|
||||
val folder: String?,
|
||||
val maps: List<String>,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
private const val BAU_LOCK_CONFIG_NAME = "baulockstate"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package de.steamwar.commands.world
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import de.steamwar.db.Database
|
||||
import de.steamwar.sql.SteamwarWorld
|
||||
import java.time.LocalTime
|
||||
|
||||
class SaveToStorageCommand : CliktCommand(name = "saveToStorage") {
|
||||
override fun run() {
|
||||
Database.ensureConnected()
|
||||
|
||||
var archived = 0
|
||||
for (world in SteamwarWorld.getWorldsToArchive()) {
|
||||
if (!beforeCutoff()) {
|
||||
echo("Stopping before 04:00. Archived $archived worlds.")
|
||||
return
|
||||
}
|
||||
|
||||
if (world.archiveIfNeeded()) {
|
||||
archived++
|
||||
echo("Archived ${world.uuid} (${world.type}/${world.name}).")
|
||||
}
|
||||
}
|
||||
|
||||
echo("Storage save completed. Archived $archived worlds.")
|
||||
}
|
||||
|
||||
private fun beforeCutoff(): Boolean =
|
||||
LocalTime.now().isBefore(LocalTime.of(4, 0))
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package de.steamwar.commands.world
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import com.github.ajalt.clikt.parameters.types.int
|
||||
import com.github.ajalt.clikt.parameters.types.path
|
||||
import de.steamwar.db.Database
|
||||
import de.steamwar.sql.SteamwarWorld
|
||||
|
||||
class TemplateCommand : CliktCommand(name = "template") {
|
||||
override fun run() = Unit
|
||||
}
|
||||
|
||||
class TemplateCreateCommand : CliktCommand(name = "create") {
|
||||
private val name by argument()
|
||||
private val version by argument().int()
|
||||
private val source by argument().path(canBeFile = false, mustExist = true)
|
||||
|
||||
override fun run() {
|
||||
Database.ensureConnected()
|
||||
SteamwarWorld.getOrCreateTemplateWorld(name, version, source.toFile())
|
||||
echo("Template world '$name' created.")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package de.steamwar.commands.world
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
|
||||
class WorldCommand : CliktCommand(name = "world") {
|
||||
override fun run() = Unit
|
||||
}
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
package de.steamwar.sql
|
||||
|
||||
import de.steamwar.sql.BauweltMemberTable.bauweltId
|
||||
import de.steamwar.sql.internal.useDb
|
||||
import org.jetbrains.exposed.v1.core.and
|
||||
import org.jetbrains.exposed.v1.core.dao.id.CompositeID
|
||||
@@ -32,45 +31,37 @@ import org.jetbrains.exposed.v1.jdbc.insertIgnore
|
||||
import java.util.*
|
||||
|
||||
object BauweltMemberTable : CompositeIdTable("BauweltMember") {
|
||||
val bauweltId = reference("BauweltID", SteamwarUserTable).index()
|
||||
val worldId = reference("WorldID", WorldTable).index()
|
||||
val memberId = reference("MemberID", SteamwarUserTable).index()
|
||||
val build = bool("Build")
|
||||
val worldEdit = bool("WorldEdit")
|
||||
val world = bool("World")
|
||||
|
||||
override val primaryKey = PrimaryKey(bauweltId, memberId)
|
||||
override val primaryKey = PrimaryKey(worldId, memberId)
|
||||
|
||||
init {
|
||||
addIdColumn(bauweltId)
|
||||
addIdColumn(worldId)
|
||||
addIdColumn(memberId)
|
||||
}
|
||||
}
|
||||
|
||||
class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
||||
companion object : CompositeEntityClass<BauweltMember>(BauweltMemberTable) {
|
||||
private val cache = mutableMapOf<Int, BauweltMember>()
|
||||
private val cache = mutableMapOf<Pair<UUID, Int>, BauweltMember>()
|
||||
|
||||
private fun cache(member: BauweltMember) =
|
||||
cache.put(member.memberID, member)
|
||||
cache.put(member.worldID to member.memberID, member)
|
||||
|
||||
@JvmStatic
|
||||
fun clear() =
|
||||
cache.clear()
|
||||
|
||||
@JvmStatic
|
||||
@Deprecated("Use addMember(ownerId: Int, newMemberId: Int)")
|
||||
fun addMember(ownerId: UUID, newMemberId: UUID) =
|
||||
addMember(SteamwarUser.get(ownerId)!!.id, SteamwarUser.get(newMemberId)!!.id)
|
||||
|
||||
@JvmStatic
|
||||
fun addMember(ownerId: Int, newMemberId: Int) =
|
||||
addMember(EntityID(ownerId, SteamwarUserTable), EntityID(newMemberId, SteamwarUserTable))
|
||||
|
||||
fun addMember(ownerId: EntityID<Int>, newMemberId: EntityID<Int>) =
|
||||
fun addMember(worldId: UUID, newMemberId: Int) =
|
||||
useDb {
|
||||
BauweltMemberTable.insertIgnore {
|
||||
it[bauweltId] = ownerId
|
||||
it[memberId] = newMemberId
|
||||
it[BauweltMemberTable.worldId] = EntityID(worldId, WorldTable)
|
||||
it[memberId] = EntityID(newMemberId, SteamwarUserTable)
|
||||
it[build] = false
|
||||
it[worldEdit] = false
|
||||
it[world] = false
|
||||
@@ -78,31 +69,32 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Deprecated("Use getBauMember(bauwelt: Int, member: Int)")
|
||||
fun getBauMember(bauwelt: UUID, member: UUID) =
|
||||
fun addMember(worldId: UUID, newMemberId: UUID) =
|
||||
addMember(worldId, SteamwarUser.get(newMemberId)!!.id.value)
|
||||
|
||||
@JvmStatic
|
||||
fun getBauMember(world: UUID, member: Int) =
|
||||
cache[world to member]
|
||||
?: useDb {
|
||||
find { (BauweltMemberTable.worldId eq world) and (BauweltMemberTable.memberId eq member) }.firstOrNull()?.also { cache(it) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getBauMember(world: UUID, member: UUID) =
|
||||
getBauMember(world, SteamwarUser.get(member)!!.id.value)
|
||||
|
||||
@JvmStatic
|
||||
fun getWorldMembers(world: UUID) =
|
||||
useDb {
|
||||
find { (bauweltId eq SteamwarUser.get(bauwelt)!!.id) and (BauweltMemberTable.memberId eq SteamwarUser.get(member)!!.id) }.firstOrNull()?.also { cache(it) }
|
||||
find { BauweltMemberTable.worldId eq world }.toList().also { it.forEach { cache(it) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getBauMember(bauwelt: Int, member: Int) =
|
||||
useDb {
|
||||
find { (bauweltId eq bauwelt) and (BauweltMemberTable.memberId eq member) }.firstOrNull()?.also { cache(it) }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Deprecated("Use getMembers(bauwelt: Int)")
|
||||
fun getMembers(bauwelt: UUID) =
|
||||
getMembers(SteamwarUser.get(bauwelt)!!.id.value)
|
||||
|
||||
@JvmStatic
|
||||
fun getMembers(bauwelt: Int) =
|
||||
useDb {
|
||||
find { bauweltId eq bauwelt }.toList().also { it.forEach { cache(it) } }
|
||||
}
|
||||
fun getMembers(world: UUID) =
|
||||
getWorldMembers(world)
|
||||
}
|
||||
|
||||
val bauweltID by BauweltMemberTable.bauweltId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
|
||||
val worldID by BauweltMemberTable.worldId.transform({ EntityID(it, WorldTable) }, { it.value })
|
||||
val memberID by BauweltMemberTable.memberId.transform({ EntityID(it, SteamwarUserTable) }, { it.value })
|
||||
private var worldEditInternal by BauweltMemberTable.worldEdit
|
||||
var worldEdit: Boolean
|
||||
@@ -144,4 +136,4 @@ class BauweltMember(id: EntityID<CompositeID>) : CompositeEntity(id) {
|
||||
useDb {
|
||||
delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ enum class UserPerm {
|
||||
PUNISHMENTS,
|
||||
TICKET_LOG,
|
||||
BUILD,
|
||||
BUILD_EXTRA_WORLDS,
|
||||
BUILD_UNLIMITED_WORLDS,
|
||||
CHECK,
|
||||
MODERATION,
|
||||
ADMINISTRATION;
|
||||
@@ -95,4 +97,4 @@ enum class UserPerm {
|
||||
}
|
||||
|
||||
data class Prefix(val colorCode: String, val chatPrefix: String, val teamPrefix: Boolean = false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.sql
|
||||
|
||||
import de.steamwar.sql.internal.useDb
|
||||
import org.jetbrains.exposed.v1.core.and
|
||||
import org.jetbrains.exposed.v1.core.dao.id.EntityID
|
||||
import org.jetbrains.exposed.v1.core.dao.id.java.UUIDTable
|
||||
import org.jetbrains.exposed.v1.core.eq
|
||||
import org.jetbrains.exposed.v1.core.not
|
||||
import org.jetbrains.exposed.v1.core.or
|
||||
import org.jetbrains.exposed.v1.core.SortOrder
|
||||
import org.jetbrains.exposed.v1.dao.Entity
|
||||
import org.jetbrains.exposed.v1.dao.EntityClass
|
||||
import org.jetbrains.exposed.v1.javatime.CurrentTimestamp
|
||||
import org.jetbrains.exposed.v1.javatime.timestamp
|
||||
import java.io.File
|
||||
import java.time.Instant
|
||||
import java.time.temporal.ChronoUnit
|
||||
import java.util.UUID
|
||||
|
||||
object WorldTable : UUIDTable("world") {
|
||||
val name = varchar("Name", 512)
|
||||
val version = integer("Version")
|
||||
val type = enumeration<WorldType>("Type")
|
||||
val owner = reference("Owner", SteamwarUserTable).nullable()
|
||||
val team = reference("Team", TeamTable).nullable()
|
||||
val lockState = varchar("LockState", 32).nullable()
|
||||
val deleted = bool("Deleted").default(false)
|
||||
val created = timestamp("Created").defaultExpression(CurrentTimestamp)
|
||||
val lastUsed = timestamp("LastUsed").defaultExpression(CurrentTimestamp)
|
||||
}
|
||||
|
||||
enum class WorldType {
|
||||
BAU,
|
||||
BUILDER,
|
||||
TEAM,
|
||||
ARENA,
|
||||
TEMPLATE,
|
||||
}
|
||||
|
||||
class SteamwarWorld(id: EntityID<UUID>) : Entity<UUID>(id) {
|
||||
var name by WorldTable.name
|
||||
private set
|
||||
var version by WorldTable.version
|
||||
private set
|
||||
var type by WorldTable.type
|
||||
private set
|
||||
var owner by WorldTable.owner
|
||||
private set
|
||||
var team by WorldTable.team
|
||||
private set
|
||||
var lockState by WorldTable.lockState
|
||||
private set
|
||||
var deleted by WorldTable.deleted
|
||||
private set
|
||||
val created by WorldTable.created
|
||||
var lastUsed by WorldTable.lastUsed
|
||||
private set
|
||||
|
||||
val archived: Boolean
|
||||
get() = !storageDirectory.exists() && archiveFile.exists()
|
||||
|
||||
val shouldArchive: Boolean
|
||||
get() = !archived && (deleted || lastUsed.plus(7, ChronoUnit.DAYS).isBefore(Instant.now()))
|
||||
|
||||
val uuid: UUID
|
||||
get() = id.value
|
||||
|
||||
val storageDirectory: File
|
||||
get() = File(WORLD_STORAGE, id.value.toString())
|
||||
|
||||
private val archiveFile: File
|
||||
get() = File(ARCHIVE_WORLD_STORAGE, "${id.value}.zip")
|
||||
|
||||
@JvmOverloads
|
||||
fun setupAndGetStoragePath(prototype: File? = null): String {
|
||||
if (archived) {
|
||||
loadFromArchive()
|
||||
}
|
||||
|
||||
val needsInitialization = !storageDirectory.exists() || storageDirectory.list()?.isEmpty() == true
|
||||
if (needsInitialization) {
|
||||
if (prototype != null && prototype.exists()) {
|
||||
prototype.copyRecursively(storageDirectory, overwrite = true)
|
||||
} else {
|
||||
storageDirectory.mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
useDb {
|
||||
lastUsed = Instant.now()
|
||||
}
|
||||
|
||||
return storageDirectory.path
|
||||
}
|
||||
|
||||
fun markDeleted() = useDb {
|
||||
deleted = true
|
||||
}
|
||||
|
||||
fun rename(newName: String) = useDb {
|
||||
name = newName
|
||||
}
|
||||
|
||||
fun archiveIfNeeded(): Boolean {
|
||||
if (!shouldArchive) return false
|
||||
|
||||
File(ARCHIVE_WORLD_STORAGE).mkdirs()
|
||||
return archiveWorld() == 0
|
||||
}
|
||||
|
||||
fun changeVersion(newVersion: Int) = useDb {
|
||||
version = newVersion
|
||||
}
|
||||
|
||||
fun changeLockState(newLockState: String?) = useDb {
|
||||
lockState = newLockState
|
||||
}
|
||||
|
||||
private fun archiveWorld() = ProcessBuilder("zip", "-u9oymrqq", "$ARCHIVE_WORLD_STORAGE/${id.value}.zip", id.value.toString())
|
||||
.directory(File(WORLD_STORAGE))
|
||||
.inheritIO()
|
||||
.start()
|
||||
.waitFor()
|
||||
|
||||
|
||||
private fun loadFromArchive() = ProcessBuilder("unzip", "-qq", "-o", "$ARCHIVE_WORLD_STORAGE/${id.value}.zip", "-d", WORLD_STORAGE)
|
||||
.inheritIO()
|
||||
.start()
|
||||
.waitFor()
|
||||
|
||||
companion object : EntityClass<UUID, SteamwarWorld>(WorldTable) {
|
||||
const val WORLD_STORAGE = "/worlds/storage"
|
||||
const val ARCHIVE_WORLD_STORAGE = "/mnt/storage/worlds/storage"
|
||||
const val DEFAULT_BAU_WORLD_LIMIT = 3
|
||||
const val EXTRA_BAU_WORLD_LIMIT = 10
|
||||
const val TEAM_WORLD_LIMIT = 2
|
||||
|
||||
@JvmStatic
|
||||
fun getWorld(uuid: UUID) = useDb {
|
||||
findById(uuid)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getBauWorld(user: SteamwarUser, version: Int) = useDb {
|
||||
find {
|
||||
(WorldTable.owner eq user.id) and
|
||||
(WorldTable.version eq version) and
|
||||
(WorldTable.type eq WorldType.BAU) and
|
||||
not(WorldTable.deleted)
|
||||
}.firstOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getBauWorld(user: SteamwarUser, name: String, version: Int) = useDb {
|
||||
find {
|
||||
(WorldTable.owner eq user.id) and
|
||||
(WorldTable.name eq name) and
|
||||
(WorldTable.version eq version) and
|
||||
(WorldTable.type eq WorldType.BAU) and
|
||||
not(WorldTable.deleted)
|
||||
}.firstOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getBauWorlds(user: SteamwarUser) = useDb {
|
||||
find {
|
||||
(WorldTable.owner eq user.id) and
|
||||
(WorldTable.type eq WorldType.BAU) and
|
||||
not(WorldTable.deleted)
|
||||
}.orderBy(WorldTable.created to SortOrder.ASC).toList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun countBauWorlds(user: SteamwarUser) = useDb {
|
||||
find {
|
||||
(WorldTable.owner eq user.id) and
|
||||
(WorldTable.type eq WorldType.BAU) and
|
||||
not(WorldTable.deleted)
|
||||
}.count()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun getOrCreateBauWorld(user: SteamwarUser, version: Int, prototype: File? = null): SteamwarWorld =
|
||||
getBauWorld(user, version) ?: createWorld(user, user.userName, version, WorldType.BAU, prototype)
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun getOrCreateBauWorld(user: SteamwarUser, name: String, version: Int, prototype: File? = null): SteamwarWorld =
|
||||
getBauWorld(user, name, version) ?: createWorld(user, name, version, WorldType.BAU, prototype)
|
||||
|
||||
@JvmStatic
|
||||
fun getBuilderWorld(name: String, version: Int) = useDb {
|
||||
find {
|
||||
(WorldTable.name eq name) and
|
||||
(WorldTable.version eq version) and
|
||||
(WorldTable.type eq WorldType.BUILDER) and
|
||||
not(WorldTable.deleted)
|
||||
}.firstOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getBuilderWorlds(version: Int) = useDb {
|
||||
find {
|
||||
(WorldTable.version eq version) and
|
||||
(WorldTable.type eq WorldType.BUILDER) and
|
||||
not(WorldTable.deleted)
|
||||
}.toList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun getOrCreateBuilderWorld(name: String, version: Int, prototype: File? = null): SteamwarWorld =
|
||||
getBuilderWorld(name, version) ?: createWorld(null, name, version, WorldType.BUILDER, prototype)
|
||||
|
||||
@JvmStatic
|
||||
fun getTeamWorld(team: Team, name: String, version: Int) = useDb {
|
||||
find {
|
||||
(WorldTable.team eq team.id) and
|
||||
(WorldTable.name eq name) and
|
||||
(WorldTable.version eq version) and
|
||||
(WorldTable.type eq WorldType.TEAM) and
|
||||
not(WorldTable.deleted)
|
||||
}.firstOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getTeamWorlds(team: Team) = useDb {
|
||||
find {
|
||||
(WorldTable.team eq team.id) and
|
||||
(WorldTable.type eq WorldType.TEAM) and
|
||||
not(WorldTable.deleted)
|
||||
}.orderBy(WorldTable.created to SortOrder.ASC).toList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun getOrCreateTeamWorld(team: Team, name: String, version: Int, prototype: File? = null): SteamwarWorld =
|
||||
getTeamWorld(team, name, version) ?: createWorld(null, name, version, WorldType.TEAM, prototype, team)
|
||||
|
||||
@JvmStatic
|
||||
fun countTeamWorlds(team: Team) = useDb {
|
||||
find {
|
||||
(WorldTable.team eq team.id) and
|
||||
(WorldTable.type eq WorldType.TEAM) and
|
||||
not(WorldTable.deleted)
|
||||
}.count()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getArenaWorld(mode: String, map: String, version: Int) = useDb {
|
||||
find {
|
||||
(WorldTable.name eq arenaWorldName(mode, map)) and
|
||||
(WorldTable.version eq version) and
|
||||
(WorldTable.type eq WorldType.ARENA) and
|
||||
not(WorldTable.deleted)
|
||||
}.firstOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun getOrCreateArenaWorld(mode: String, map: String, version: Int, prototype: File? = null): SteamwarWorld =
|
||||
getArenaWorld(mode, map, version) ?: createWorld(null, arenaWorldName(mode, map), version, WorldType.ARENA, prototype)
|
||||
|
||||
@JvmStatic
|
||||
fun getTemplateWorld(name: String, version: Int) = useDb {
|
||||
find {
|
||||
(WorldTable.name eq name) and
|
||||
(WorldTable.version eq version) and
|
||||
(WorldTable.type eq WorldType.TEMPLATE) and
|
||||
not(WorldTable.deleted)
|
||||
}.firstOrNull()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getTemplateWorlds() = useDb {
|
||||
find {
|
||||
(WorldTable.type eq WorldType.TEMPLATE) and
|
||||
not(WorldTable.deleted)
|
||||
}.orderBy(WorldTable.name to SortOrder.ASC).toList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getTemplateWorlds(version: Int) = useDb {
|
||||
find {
|
||||
(WorldTable.version eq version) and
|
||||
(WorldTable.type eq WorldType.TEMPLATE) and
|
||||
not(WorldTable.deleted)
|
||||
}.orderBy(WorldTable.name to SortOrder.ASC).toList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun getOrCreateTemplateWorld(name: String, version: Int, prototype: File? = null): SteamwarWorld =
|
||||
getTemplateWorld(name, version) ?: createWorld(null, name, version, WorldType.TEMPLATE, prototype)
|
||||
|
||||
@JvmStatic
|
||||
fun getAllActiveWorlds() = useDb {
|
||||
find { not(WorldTable.deleted) }.toList()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getWorldsToArchive() = useDb {
|
||||
find {
|
||||
not(WorldTable.deleted) or (WorldTable.deleted eq true)
|
||||
}.toList().filter { it.shouldArchive }
|
||||
}
|
||||
|
||||
private fun arenaWorldName(mode: String, map: String) = "$mode/$map"
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun createWorld(user: SteamwarUser?, name: String, version: Int, type: WorldType, prototype: File? = null, team: Team? = null): SteamwarWorld {
|
||||
val world = useDb { new {
|
||||
this.name = name
|
||||
this.version = version
|
||||
this.type = type
|
||||
this.owner = user?.id
|
||||
this.team = team?.id
|
||||
} }
|
||||
|
||||
world.setupAndGetStoragePath(prototype)
|
||||
return world
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,6 +110,7 @@ public class FightSystem extends JavaPlugin {
|
||||
|
||||
hullHider = new HullHider();
|
||||
techHider = new TechHiderWrapper(hullHider);
|
||||
FightSystem.getHullHider().getHullMap().values().forEach(hull -> hull.fill(true));
|
||||
|
||||
|
||||
FileSource.startReplay();
|
||||
|
||||
@@ -158,6 +158,8 @@ public class FightSchematic extends StateDependent {
|
||||
FreezeWorld freezer = new FreezeWorld();
|
||||
|
||||
team.teleportToSpawn();
|
||||
// TODO: Implement hull generation based on clipboard content!
|
||||
FightSystem.getHullHider().fill(team, false);
|
||||
Vector dims = WorldeditWrapper.impl.getDimensions(clipboard);
|
||||
WorldeditWrapper.impl.pasteClipboard(
|
||||
clipboard,
|
||||
|
||||
@@ -68,6 +68,13 @@ public class FightWorld extends StateDependent {
|
||||
}
|
||||
|
||||
public static void resetWorld() {
|
||||
World backup = new WorldCreator(Config.world.getName() + "/backup").createWorld();
|
||||
assert backup != null;
|
||||
Config.ArenaRegion.forEachChunk((x, z) -> {
|
||||
CraftbukkitWrapper.impl.resetChunk(Config.world, backup, x, z);
|
||||
});
|
||||
Bukkit.unloadWorld(backup, false);
|
||||
|
||||
List<Entity> entities = new ArrayList<>();
|
||||
Recording.iterateOverEntities(Objects::nonNull, entity -> {
|
||||
if (entity.getType() != EntityType.PLAYER && (!Config.GameModeConfig.Arena.Leaveable || Config.ArenaRegion.inRegion(entity.getLocation()))) {
|
||||
@@ -77,14 +84,12 @@ public class FightWorld extends StateDependent {
|
||||
entities.forEach(Entity::remove);
|
||||
entities.clear();
|
||||
|
||||
World backup = new WorldCreator(Config.world.getName() + "/backup").createWorld();
|
||||
assert backup != null;
|
||||
FightSystem.getHullHider().getHullMap().values().forEach(hull -> hull.fill(true));
|
||||
|
||||
Config.ArenaRegion.forEachChunk((x, z) -> {
|
||||
CraftbukkitWrapper.impl.resetChunk(Config.world, backup, x, z);
|
||||
for (Player p : Bukkit.getOnlinePlayers()) {
|
||||
de.steamwar.core.CraftbukkitWrapper.sendChunk(p, x, z);
|
||||
}
|
||||
});
|
||||
Bukkit.unloadWorld(backup, false);
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ public class ArrowStopper {
|
||||
private void run() {
|
||||
Recording.iterateOverEntities(AbstractArrow.class::isInstance, entity -> {
|
||||
Projectile arrow = (Projectile) entity;
|
||||
if(!(arrow.getShooter() instanceof Player)) return;
|
||||
if (invalidEntity(arrow)) return;
|
||||
|
||||
Location prevLocation = arrow.getLocation().toVector().subtract(arrow.getVelocity()).toLocation(arrow.getWorld());
|
||||
|
||||
+2
-1
@@ -24,6 +24,7 @@ import de.steamwar.fightsystem.Config;
|
||||
import de.steamwar.fightsystem.utils.CraftbukkitWrapper;
|
||||
import de.steamwar.linkage.Linked;
|
||||
import net.minecraft.network.protocol.game.ServerboundUseItemOnPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.*;
|
||||
@@ -42,7 +43,7 @@ public class ClickAnalyzer {
|
||||
}
|
||||
|
||||
public ClickAnalyzer() {
|
||||
TinyProtocol.instance.addFilter(Recording.blockPlacePacket, this::onBlockPlace);
|
||||
TinyProtocol.instance.addFilter(ServerboundUseItemPacket.class, this::onBlockPlace);
|
||||
TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, this::onBlockPlace);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import de.steamwar.fightsystem.states.StateDependentListener;
|
||||
import de.steamwar.fightsystem.states.StateDependentTask;
|
||||
import de.steamwar.fightsystem.utils.SWSound;
|
||||
import de.steamwar.linkage.Linked;
|
||||
import net.minecraft.network.protocol.Packet;
|
||||
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
|
||||
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
@@ -124,7 +125,7 @@ public class Recording implements Listener {
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
TinyProtocol.instance.removeFilter(blockPlacePacket, place);
|
||||
TinyProtocol.instance.removeFilter(ServerboundUseItemPacket.class, place);
|
||||
TinyProtocol.instance.removeFilter(blockDigPacket, dig);
|
||||
}
|
||||
}.register();
|
||||
@@ -142,7 +143,7 @@ public class Recording implements Listener {
|
||||
GlobalRecorder.getInstance().entitySpeed(entity);
|
||||
}
|
||||
|
||||
private static final Class<?> blockDigPacket = ServerboundPlayerActionPacket.class;
|
||||
private static final Class<? extends Packet<?>> blockDigPacket = ServerboundPlayerActionPacket.class;
|
||||
private static final Class<?> playerDigType = blockDigPacket.getDeclaredClasses()[0];
|
||||
private static final Reflection.Field<?> blockDigType = Reflection.getField(blockDigPacket, playerDigType, 0);
|
||||
private static final Object releaseUseItem = playerDigType.getEnumConstants()[5];
|
||||
@@ -154,8 +155,6 @@ public class Recording implements Listener {
|
||||
return packet;
|
||||
}
|
||||
|
||||
public static final Class<?> blockPlacePacket = ServerboundUseItemPacket.class;
|
||||
|
||||
private Object blockPlace(Player p, ServerboundUseItemPacket packet) {
|
||||
boolean mainHand = packet.getHand() == InteractionHand.MAIN_HAND;
|
||||
if (!isNotSent(p) && (mainHand ? p.getInventory().getItemInMainHand() : p.getInventory().getItemInOffHand()).getType() == Material.BOW) {
|
||||
|
||||
@@ -165,6 +165,15 @@ public class Hull {
|
||||
rentities.remove(entity);
|
||||
}
|
||||
|
||||
public void fill(boolean visible) {
|
||||
visibility.set(0, visibility.size(), visible);
|
||||
occluding.set(0, occluding.size(), !visible);
|
||||
uncoveredSurface.clear();
|
||||
for (BitSet directionalVisibility : visibilityDirections.values()) {
|
||||
directionalVisibility.set(0, directionalVisibility.size(), visible);
|
||||
}
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
visibility.clear();
|
||||
occluding.clear();
|
||||
|
||||
@@ -19,8 +19,6 @@
|
||||
|
||||
package de.steamwar.fightsystem.utils;
|
||||
|
||||
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||
import de.steamwar.Reflection;
|
||||
import de.steamwar.entity.REntity;
|
||||
import de.steamwar.fightsystem.Config;
|
||||
import de.steamwar.fightsystem.fight.Fight;
|
||||
@@ -28,17 +26,9 @@ import de.steamwar.fightsystem.fight.FightPlayer;
|
||||
import de.steamwar.fightsystem.fight.FightTeam;
|
||||
import de.steamwar.fightsystem.listener.Recording;
|
||||
import de.steamwar.fightsystem.states.FightState;
|
||||
import de.steamwar.fightsystem.states.StateDependent;
|
||||
import de.steamwar.fightsystem.states.StateDependentListener;
|
||||
import de.steamwar.fightsystem.states.StateDependentTask;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.network.protocol.game.ClientboundExplodePacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelEventPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket;
|
||||
import net.minecraft.network.protocol.game.ClientboundSoundPacket;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -54,8 +44,6 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class HullHider implements Listener {
|
||||
|
||||
@@ -79,10 +67,13 @@ public class HullHider implements Listener {
|
||||
|
||||
public void initialize(FightTeam team) {
|
||||
if (!TechHiderWrapper.ENABLED) return;
|
||||
|
||||
hullMap.get(team).initialize();
|
||||
}
|
||||
|
||||
public void fill(FightTeam team, boolean visible) {
|
||||
if (!TechHiderWrapper.ENABLED) return;
|
||||
hullMap.get(team).fill(visible);
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH)
|
||||
public void onJoin(PlayerJoinEvent e) {
|
||||
|
||||
+67
-31
@@ -22,7 +22,6 @@ package de.steamwar.fightsystem.utils;
|
||||
import de.steamwar.Reflection;
|
||||
import de.steamwar.core.CraftbukkitWrapper;
|
||||
import de.steamwar.fightsystem.Config;
|
||||
import de.steamwar.fightsystem.FightSystem;
|
||||
import de.steamwar.fightsystem.events.BoardingEvent;
|
||||
import de.steamwar.fightsystem.events.TeamLeaveEvent;
|
||||
import de.steamwar.fightsystem.events.TeamSpawnEvent;
|
||||
@@ -31,6 +30,7 @@ import de.steamwar.fightsystem.fight.FightTeam;
|
||||
import de.steamwar.fightsystem.states.FightState;
|
||||
import de.steamwar.fightsystem.states.StateDependent;
|
||||
import de.steamwar.fightsystem.states.StateDependentListener;
|
||||
import de.steamwar.techhider.AccessPrivilegeProvider;
|
||||
import de.steamwar.techhider.TechHider;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.core.Holder;
|
||||
@@ -38,6 +38,11 @@ import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -45,10 +50,12 @@ import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class TechHiderWrapper extends StateDependent implements Listener {
|
||||
|
||||
@@ -56,11 +63,21 @@ public class TechHiderWrapper extends StateDependent implements Listener {
|
||||
|
||||
@Getter
|
||||
private final ConcurrentHashMap<Player, Region> hiddenRegion = new ConcurrentHashMap<>();
|
||||
private final TechHider techHider;
|
||||
|
||||
private final HullHider hullHider;
|
||||
|
||||
public TechHiderWrapper(HullHider hullHider) {
|
||||
super(ENABLED, FightState.Schem);
|
||||
super(ENABLED, FightState.All);
|
||||
|
||||
this.hullHider = hullHider;
|
||||
|
||||
new StateDependentListener(ENABLED, FightState.All, this);
|
||||
register();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
Set<BlockState> blockStatesToObfuscate = getBlockStatesToHideFromMaterials(Config.GameModeConfig.Techhider.HiddenBlocks);
|
||||
|
||||
Set<Block> blocksToObfuscate = Config.GameModeConfig.Techhider.HiddenBlocks.stream()
|
||||
.map(CraftMagicNumbers::getBlock)
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
@@ -72,14 +89,19 @@ public class TechHiderWrapper extends StateDependent implements Listener {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
Reflection.Method method = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "get", Optional.class, ResourceLocation.class);
|
||||
Set<BlockEntityType<?>> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream()
|
||||
Set<BlockEntityType<?>> blockEntityTypeToObfuscate = Config.GameModeConfig.Techhider.HiddenBlockEntities.stream()
|
||||
.map((id) -> {
|
||||
ResourceLocation loc = ResourceLocation.parse(id);
|
||||
return ((Optional<Holder.Reference<BlockEntityType<?>>>) method.invoke(blockEntityType, loc)).get().value();
|
||||
})
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
|
||||
techHider = new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith)) {
|
||||
new TechHider(CraftMagicNumbers.getBlock(Config.GameModeConfig.Techhider.ObfuscateWith), new AccessPrivilegeProvider() {
|
||||
@Override
|
||||
public boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ) {
|
||||
return Fight.teams().stream().map(FightTeam::getExtendRegion).allMatch(region -> region.chunkOutside(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ) {
|
||||
return !hullHider.isBlockHidden(p, blockX, blockY, blockZ);
|
||||
@@ -87,41 +109,36 @@ public class TechHiderWrapper extends StateDependent implements Listener {
|
||||
|
||||
@Override
|
||||
public boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block) {
|
||||
Region hiddenRegion = getHiddenRegion(p);
|
||||
return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block);
|
||||
return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blocksToObfuscate.contains(block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayerPrivilegedToAccessBlockState(Player p, int blockX, int blockY, int blockZ, BlockState blockState) {
|
||||
return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blockStatesToObfuscate.contains(blockState);
|
||||
}
|
||||
|
||||
// TODO will require entity tracking on the netty thread to prevent future race conditions
|
||||
@Override
|
||||
public boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId) {
|
||||
net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld) p.getWorld()).getHandle().moonrise$getEntityLookup().get(entityId);
|
||||
net.minecraft.world.entity.Entity nmsEntity = ((CraftWorld) p.getWorld()).getHandle().moonrise$getEntityLookup().get(entityId);
|
||||
|
||||
if(nmsEntity != null) {
|
||||
if (nmsEntity != null) {
|
||||
return !hullHider.isBlockHidden(p, nmsEntity.getBlockX(), nmsEntity.getBlockY(), nmsEntity.getBlockZ());
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType<?> type) {
|
||||
Region hiddenRegion = getHiddenRegion(p);
|
||||
return !hiddenRegion.inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type);
|
||||
public boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType<?> type) {
|
||||
return !getHiddenRegion(p).inRegion(blockX, blockY, blockZ) || !blockEntityTypeToObfuscate.contains(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ) {
|
||||
return getHiddenRegions().stream().allMatch(region -> region.chunkOutside(chunkX, chunkZ));
|
||||
public boolean isPlayerPrivilegedToPerformAction(Player p) {
|
||||
return p.getGameMode() != GameMode.SPECTATOR;
|
||||
}
|
||||
};
|
||||
|
||||
new StateDependentListener(ENABLED, FightState.Schem, this);
|
||||
register();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enable() {
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -164,10 +181,6 @@ public class TechHiderWrapper extends StateDependent implements Listener {
|
||||
});
|
||||
}
|
||||
|
||||
private Set<Region> getHiddenRegions() {
|
||||
return Fight.teams().stream().map(FightTeam::getExtendRegion).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
private Region getHiddenRegion(Player player) {
|
||||
if (Config.isReferee(player)) return Region.EMPTY;
|
||||
|
||||
@@ -178,4 +191,27 @@ public class TechHiderWrapper extends StateDependent implements Listener {
|
||||
|
||||
return Fight.getOpposite(team).getExtendRegion();
|
||||
}
|
||||
|
||||
private Stream<BlockState> getWaterloggedBlockStates() {
|
||||
FluidState waterFluidState = Fluids.WATER.getSource(false);
|
||||
|
||||
return BuiltInRegistries.BLOCK.stream()
|
||||
.map((block) -> block.getStateDefinition().getPossibleStates())
|
||||
.flatMap(Collection::stream)
|
||||
.filter((blockState -> blockState.getFluidState() == waterFluidState));
|
||||
}
|
||||
|
||||
private Set<BlockState> getBlockStatesToHideFromMaterials(Set<Material> materials) {
|
||||
Stream<BlockState> allStatesFromMaterials = materials.stream()
|
||||
.map(CraftMagicNumbers::getBlock)
|
||||
.map((block) -> block.getStateDefinition().getPossibleStates())
|
||||
.flatMap(Collection::stream);
|
||||
|
||||
if(materials.contains(Material.WATER)) {
|
||||
return Stream.concat(allStatesFromMaterials, getWaterloggedBlockStates()).collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
else {
|
||||
return allStatesFromMaterials.collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ tasks.register<FightServer>("WarGear21") {
|
||||
dependsOn(":KotlinCore:shadowJar")
|
||||
template = "WarGear21"
|
||||
worldName = "arenas/Pentraki"
|
||||
config = "WarGear20.yml"
|
||||
config = "WarGear21.yml"
|
||||
jar = "/jars/paper-1.21.6.jar"
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
package de.steamwar.lobby.boatrace;
|
||||
|
||||
import de.steamwar.entity.REntity;
|
||||
import de.steamwar.entity.REntityAction;
|
||||
import de.steamwar.entity.REntityServer;
|
||||
import de.steamwar.entity.RInteraction;
|
||||
import de.steamwar.lobby.LobbySystem;
|
||||
import de.steamwar.lobby.util.LeaderboardManager;
|
||||
import de.steamwar.sql.SteamwarUser;
|
||||
@@ -58,11 +60,12 @@ public class BoatRace implements EventListener, Listener {
|
||||
|
||||
static {
|
||||
boatNpcServer = new REntityServer();
|
||||
REntity starter = new REntity(boatNpcServer, EntityType.VILLAGER, BoatRacePositions.NPC);
|
||||
boatNpcServer.setCallback((player, rEntity, entityAction) -> {
|
||||
if (rEntity != starter) return;
|
||||
Bukkit.getWorlds().get(0).getEntities().stream().filter(entity -> entity.getType() == EntityType.END_CRYSTAL).forEach(Entity::remove);
|
||||
if (entityAction == REntityServer.EntityAction.INTERACT && !oneNotStarted) {
|
||||
new REntity(boatNpcServer, EntityType.VILLAGER, BoatRacePositions.NPC);
|
||||
RInteraction interaction = new RInteraction(boatNpcServer, BoatRacePositions.NPC.clone().subtract(0.5, 0, 0.5));
|
||||
interaction.setInteractionHeight(1.95f);
|
||||
interaction.setCallback((player, entity, action) -> {
|
||||
Bukkit.getWorlds().get(0).getEntities().stream().filter(e -> e.getType() == EntityType.END_CRYSTAL).forEach(Entity::remove);
|
||||
if (action == REntityAction.INTERACT && !oneNotStarted) {
|
||||
oneNotStarted = true;
|
||||
new BoatRace(player);
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ public class TinyProtocol {
|
||||
}
|
||||
}
|
||||
|
||||
public <T> void addFilter(Class<T> packetType, BiFunction<Player, ? super T, Object> filter) {
|
||||
public <T extends Packet<?>> void addFilter(Class<T> packetType, BiFunction<Player, ? super T, Object> filter) {
|
||||
packetFilters.computeIfAbsent(packetType, c -> new CopyOnWriteArrayList<>()).add((BiFunction) filter);
|
||||
}
|
||||
|
||||
@@ -235,8 +235,8 @@ public class TinyProtocol {
|
||||
packetFilters.getOrDefault(packetType, Collections.emptyList()).remove(filter);
|
||||
}
|
||||
|
||||
public void addGlobalClientboundFilter(BiFunction<Player, Object, Object> filter) {
|
||||
globalClientboundFilters.add(filter);
|
||||
public void addGlobalClientboundFilter(BiFunction<Player, Packet<?>, Object> filter) {
|
||||
globalClientboundFilters.add((BiFunction) filter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -453,7 +453,7 @@ public class TinyProtocol {
|
||||
try {
|
||||
msg = filterPacket(player, msg);
|
||||
|
||||
if(msg instanceof Packet<?>) {
|
||||
if (msg instanceof Packet<?>) {
|
||||
for (BiFunction<Player, Object, Object> filter : globalClientboundFilters) {
|
||||
msg = filter.apply(player, msg);
|
||||
if (msg == null) break;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
package de.steamwar.entity;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||
import org.bukkit.Location;
|
||||
@@ -27,13 +28,16 @@ import org.bukkit.entity.EntityType;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class RArmorStand extends REntity {
|
||||
@Getter
|
||||
public class RArmorStand extends REntity implements RInteractableEntity<RArmorStand> {
|
||||
|
||||
private static final EntityDataAccessor<Byte> sizeWatcher = new EntityDataAccessor<>(15, EntityDataSerializers.BYTE);
|
||||
|
||||
@Getter
|
||||
private final Size size;
|
||||
|
||||
@Setter
|
||||
private REntityActionListener<RArmorStand> callback = null;
|
||||
|
||||
public RArmorStand(REntityServer server, Location location, Size size) {
|
||||
super(server, EntityType.ARMOR_STAND, location, 0);
|
||||
this.size = size;
|
||||
|
||||
+5
-16
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -17,20 +17,9 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.bausystem.utils;
|
||||
package de.steamwar.entity;
|
||||
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
|
||||
public class TickStartEvent extends Event {
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
public enum REntityAction {
|
||||
INTERACT,
|
||||
ATTACK,
|
||||
}
|
||||
+5
-15
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -17,20 +17,10 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.bausystem.utils;
|
||||
package de.steamwar.entity;
|
||||
|
||||
import org.bukkit.event.Event;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class TickEndEvent extends Event {
|
||||
|
||||
private static final HandlerList handlers = new HandlerList();
|
||||
|
||||
public HandlerList getHandlers() {
|
||||
return handlers;
|
||||
}
|
||||
|
||||
public static HandlerList getHandlerList() {
|
||||
return handlers;
|
||||
}
|
||||
public interface REntityActionListener<E extends REntity> {
|
||||
void onAction(Player player, E entity, REntityAction action);
|
||||
}
|
||||
@@ -51,7 +51,6 @@ public class REntityServer implements Listener {
|
||||
private final HashMap<Player, Location> lastLocation = new HashMap<>();
|
||||
private final HashMap<Player, Integer> viewDistance = new HashMap<>();
|
||||
|
||||
private EntityActionListener callback = null;
|
||||
private final Set<Player> playersThatClicked = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private final BiFunction<Player, ServerboundInteractPacket, Object> filter = (player, packet) -> {
|
||||
@@ -61,25 +60,19 @@ public class REntityServer implements Listener {
|
||||
if (playersThatClicked.contains(player)) return null;
|
||||
|
||||
playersThatClicked.add(player);
|
||||
EntityAction action = packet.isAttack() ? EntityAction.ATTACK : EntityAction.INTERACT;
|
||||
REntityAction action = packet.isAttack() ? REntityAction.ATTACK : REntityAction.INTERACT;
|
||||
Bukkit.getScheduler().runTask(Core.getInstance(), () -> {
|
||||
playersThatClicked.remove(player);
|
||||
callback.onAction(player, entity, action);
|
||||
if (entity instanceof RInteractableEntity interactable && interactable.getCallback() != null) {
|
||||
interactable.getCallback().onAction(player, entity, action);
|
||||
}
|
||||
});
|
||||
return null;
|
||||
};
|
||||
|
||||
public REntityServer() {
|
||||
Core.getInstance().getServer().getPluginManager().registerEvents(this, Core.getInstance());
|
||||
}
|
||||
|
||||
public void setCallback(EntityActionListener callback) {
|
||||
boolean uninitialized = this.callback == null;
|
||||
this.callback = callback;
|
||||
|
||||
if (uninitialized) {
|
||||
TinyProtocol.instance.addFilter(ServerboundInteractPacket.class, filter);
|
||||
}
|
||||
TinyProtocol.instance.addFilter(ServerboundInteractPacket.class, filter);
|
||||
}
|
||||
|
||||
public void addPlayer(Player player) {
|
||||
@@ -293,13 +286,4 @@ public class REntityServer implements Listener {
|
||||
private long chunkToId(int x, int z) {
|
||||
return ((long) x << 32) + z;
|
||||
}
|
||||
|
||||
public enum EntityAction {
|
||||
INTERACT,
|
||||
ATTACK,
|
||||
}
|
||||
|
||||
public interface EntityActionListener {
|
||||
void onAction(Player player, REntity entity, EntityAction action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,16 +20,21 @@
|
||||
package de.steamwar.entity;
|
||||
|
||||
import de.steamwar.techhider.BlockIds;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
@Getter
|
||||
public class RFallingBlockEntity extends REntity {
|
||||
public class RFallingBlockEntity extends REntity implements RInteractableEntity<RFallingBlockEntity> {
|
||||
|
||||
private final Material material;
|
||||
|
||||
@Setter
|
||||
private REntityActionListener<RFallingBlockEntity> callback = null;
|
||||
|
||||
public RFallingBlockEntity(REntityServer server, Location location, Material material) {
|
||||
super(server, EntityType.FALLING_BLOCK, location, BlockIds.impl.materialToId(material));
|
||||
this.material = material;
|
||||
|
||||
+11
-11
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2025 SteamWar.de-Serverteam
|
||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
@@ -17,18 +17,18 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.techhider;
|
||||
package de.steamwar.entity;
|
||||
|
||||
import de.steamwar.Reflection;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
public class ProtocolWrapper {
|
||||
public static final ProtocolWrapper impl = new ProtocolWrapper();
|
||||
public interface RInteractableEntity<E extends REntity> {
|
||||
REntityActionListener<E> getCallback();
|
||||
|
||||
void setCallback(REntityActionListener<E> callback);
|
||||
|
||||
default void setCallback(BiConsumer<Player, REntityAction> callback) {
|
||||
setCallback((player, interaction, entityAction) -> callback.accept(player, entityAction));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.entity;
|
||||
|
||||
import de.steamwar.core.BountifulWrapper;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* !! This class cannot be used in Versions lower than or equal to 1.19.4 !!
|
||||
*/
|
||||
@Getter
|
||||
public class RInteraction extends REntity implements RInteractableEntity<RInteraction> {
|
||||
|
||||
protected final Consumer<Object> updatePacketSink = o -> server.updateEntity(this, o);
|
||||
|
||||
@Setter
|
||||
private REntityActionListener<RInteraction> callback = null;
|
||||
|
||||
private float interactionWidth = 1.0f;
|
||||
private float interactionHeight = 1.0f;
|
||||
private boolean responsive = false;
|
||||
|
||||
public RInteraction(REntityServer server, Location location) {
|
||||
super(server, EntityType.INTERACTION, location);
|
||||
server.addEntity(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postSpawn(Consumer<Object> packetSink) {
|
||||
super.postSpawn(packetSink);
|
||||
sendPacket(packetSink,
|
||||
this::getInteractionWidthData,
|
||||
this::getInteractionHeightData,
|
||||
this::getResponsiveData
|
||||
);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
protected final void sendPacket(Consumer<Object> packetSink, BiConsumer<Boolean, BiConsumer<Object, Object>>... dataSinkSinks) {
|
||||
List<Object> keyValueData = new ArrayList<>();
|
||||
boolean ignoreDefault = packetSink == updatePacketSink;
|
||||
for (BiConsumer<Boolean, BiConsumer<Object, Object>> dataSinkSink : dataSinkSinks) {
|
||||
dataSinkSink.accept(ignoreDefault, (dataWatcher, value) -> {
|
||||
keyValueData.add(dataWatcher);
|
||||
keyValueData.add(value);
|
||||
});
|
||||
}
|
||||
if (!keyValueData.isEmpty()) {
|
||||
packetSink.accept(getDataWatcherPacket(keyValueData.toArray()));
|
||||
}
|
||||
}
|
||||
|
||||
public void setInteractionWidth(float interactionWidth) {
|
||||
this.interactionWidth = interactionWidth;
|
||||
sendPacket(updatePacketSink, this::getInteractionWidthData);
|
||||
}
|
||||
|
||||
private static final Object interactionWidthWatcher = BountifulWrapper.impl.getDataWatcherObject(8, Float.class);
|
||||
|
||||
private void getInteractionWidthData(boolean ignoreDefault, BiConsumer<Object, Object> dataSink) {
|
||||
if (ignoreDefault || interactionWidth != 1.0) {
|
||||
dataSink.accept(interactionWidthWatcher, interactionWidth);
|
||||
}
|
||||
}
|
||||
|
||||
public void setInteractionHeight(float interactionHeight) {
|
||||
this.interactionHeight = interactionHeight;
|
||||
sendPacket(updatePacketSink, this::getInteractionHeightData);
|
||||
}
|
||||
|
||||
private static final Object interactionHeightWatcher = BountifulWrapper.impl.getDataWatcherObject(9, Float.class);
|
||||
|
||||
private void getInteractionHeightData(boolean ignoreDefault, BiConsumer<Object, Object> dataSink) {
|
||||
if (ignoreDefault || interactionHeight != 1.0) {
|
||||
dataSink.accept(interactionHeightWatcher, interactionHeight);
|
||||
}
|
||||
}
|
||||
|
||||
public void setResponsive(boolean responsive) {
|
||||
this.responsive = responsive;
|
||||
sendPacket(updatePacketSink, this::getResponsiveData);
|
||||
}
|
||||
|
||||
private static final Object responsiveWatcher = BountifulWrapper.impl.getDataWatcherObject(10, Boolean.class);
|
||||
|
||||
private void getResponsiveData(boolean ignoreDefault, BiConsumer<Object, Object> dataSink) {
|
||||
if (ignoreDefault || !responsive) {
|
||||
dataSink.accept(responsiveWatcher, responsive);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import de.steamwar.network.CoreNetworkHandler;
|
||||
import de.steamwar.network.NetworkSender;
|
||||
import de.steamwar.network.packets.common.PlayerSkinRequestPacket;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.GameMode;
|
||||
@@ -39,7 +40,7 @@ import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class RPlayer extends REntity {
|
||||
public class RPlayer extends REntity implements RInteractableEntity<RPlayer> {
|
||||
|
||||
private static final Object skinPartsDataWatcher = BountifulWrapper.impl.getDataWatcherObject(17, Byte.class);
|
||||
|
||||
@@ -48,6 +49,10 @@ public class RPlayer extends REntity {
|
||||
@Getter
|
||||
private final String name;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
private REntityActionListener<RPlayer> callback = null;
|
||||
|
||||
public RPlayer(REntityServer server, UUID uuid, String name, Location location) {
|
||||
super(server, EntityType.PLAYER, UUID.nameUUIDFromBytes(uuid.toString().getBytes(StandardCharsets.UTF_8)), location, 0);
|
||||
this.actualUUID = uuid;
|
||||
|
||||
@@ -20,14 +20,54 @@
|
||||
package de.steamwar.providers;
|
||||
|
||||
import de.steamwar.sql.SteamwarUser;
|
||||
import de.steamwar.sql.WorldType;
|
||||
import org.bukkit.Bukkit;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class BauServerInfo {
|
||||
private static Integer bauOwner = null;
|
||||
private static Integer bauTeam = null;
|
||||
private static UUID bauWorld = null;
|
||||
private static WorldType bauType = null;
|
||||
|
||||
static {
|
||||
String ownerProperty = System.getProperty("bauOwner");
|
||||
if (ownerProperty != null && !ownerProperty.isBlank()) {
|
||||
try {
|
||||
bauOwner = Integer.parseInt(ownerProperty);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
String teamProperty = System.getProperty("bauTeam");
|
||||
if (teamProperty != null && !teamProperty.isBlank()) {
|
||||
try {
|
||||
bauTeam = Integer.parseInt(teamProperty);
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
String worldProperty = System.getProperty("bauWorld");
|
||||
if (worldProperty != null && !worldProperty.isBlank()) {
|
||||
try {
|
||||
bauWorld = UUID.fromString(worldProperty);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
String typeProperty = System.getProperty("bauType");
|
||||
if (typeProperty != null && !typeProperty.isBlank()) {
|
||||
try {
|
||||
bauType = WorldType.valueOf(typeProperty);
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
bauOwner = Integer.parseInt(Bukkit.getWorlds().get(0).getName());
|
||||
if (bauOwner == null) {
|
||||
bauOwner = Integer.parseInt(Bukkit.getWorlds().get(0).getName());
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
}
|
||||
@@ -37,11 +77,23 @@ public class BauServerInfo {
|
||||
}
|
||||
|
||||
public static boolean isBauServer() {
|
||||
return bauOwner != null;
|
||||
return bauOwner != null || bauWorld != null || bauTeam != null;
|
||||
}
|
||||
|
||||
public static SteamwarUser getOwnerUser() {
|
||||
if (bauOwner == null) return null;
|
||||
return SteamwarUser.byId(bauOwner);
|
||||
}
|
||||
|
||||
public static UUID getWorldId() {
|
||||
return bauWorld;
|
||||
}
|
||||
|
||||
public static Integer getTeamId() {
|
||||
return bauTeam;
|
||||
}
|
||||
|
||||
public static WorldType getWorldType() {
|
||||
return bauType;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.techhider;
|
||||
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public interface AccessPrivilegeProvider {
|
||||
boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ);
|
||||
|
||||
boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ);
|
||||
|
||||
boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block);
|
||||
|
||||
boolean isPlayerPrivilegedToAccessBlockState(Player p, int blockX, int blockY, int blockZ, BlockState blockState);
|
||||
|
||||
boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId);
|
||||
|
||||
boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType<?> type);
|
||||
|
||||
boolean isPlayerPrivilegedToPerformAction(Player p);
|
||||
}
|
||||
@@ -19,17 +19,12 @@
|
||||
|
||||
package de.steamwar.techhider;
|
||||
|
||||
import de.steamwar.Reflection;
|
||||
import net.minecraft.core.IdMapper;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Registry;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Waterlogged;
|
||||
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
@@ -22,9 +22,6 @@ package de.steamwar.techhider;
|
||||
import de.steamwar.Reflection;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
@@ -34,11 +31,10 @@ import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
|
||||
public abstract class ChunkHider {
|
||||
public class ChunkHider {
|
||||
private static final UnaryOperator<Object> chunkPacketShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkWithLightPacket.class);
|
||||
private static final UnaryOperator<Object> chunkDataShallowCloner = ProtocolUtils.shallowCloneGenerator(ClientboundLevelChunkPacketData.class);
|
||||
|
||||
@@ -54,16 +50,16 @@ public abstract class ChunkHider {
|
||||
private final int BLOCKS_PER_SECTION = 4096;
|
||||
private final int BIOMES_PER_SECTION = 64;
|
||||
|
||||
private final byte BITS_PER_LONG = 64;
|
||||
|
||||
private final int blockIdUsedForHiding;
|
||||
private final AccessPrivilegeProvider accessPrivilegeProvider;
|
||||
|
||||
public ChunkHider(Block blockUsedForObfuscation) {
|
||||
blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState());
|
||||
public ChunkHider(Block blockUsedForObfuscation, AccessPrivilegeProvider accessPrivilegeProvider) {
|
||||
blockIdUsedForHiding = Block.BLOCK_STATE_REGISTRY.getId(blockUsedForObfuscation.defaultBlockState());
|
||||
this.accessPrivilegeProvider = accessPrivilegeProvider;
|
||||
}
|
||||
|
||||
private int getLongsRequiredToEncodeEntries(int bitsPerEntry, int entryCount) {
|
||||
int entriesPerLong = BITS_PER_LONG / bitsPerEntry;
|
||||
int entriesPerLong = Long.SIZE / bitsPerEntry;
|
||||
int dataLengthAsLongCount = (entryCount + entriesPerLong - 1) / entriesPerLong;
|
||||
|
||||
return dataLengthAsLongCount;
|
||||
@@ -73,7 +69,7 @@ public abstract class ChunkHider {
|
||||
int dataLengthAsLongCount = getLongsRequiredToEncodeEntries(bitsPerEntry, entryCount);
|
||||
long[] dataArray = new long[dataLengthAsLongCount];
|
||||
|
||||
for(int i = 0; i < dataLengthAsLongCount; i++){
|
||||
for (int i = 0; i < dataLengthAsLongCount; i++) {
|
||||
dataArray[i] = dataSource.readLong();
|
||||
}
|
||||
|
||||
@@ -96,22 +92,21 @@ public abstract class ChunkHider {
|
||||
|
||||
byte bitsPerBlock = in.readByte();
|
||||
|
||||
if(bitsPerBlock == 0) {
|
||||
if (bitsPerBlock == 0) {
|
||||
int sectionBlockId = ProtocolUtils.readVarInt(in);
|
||||
|
||||
out.writeShort(blockCount);
|
||||
out.writeByte(bitsPerBlock);
|
||||
ProtocolUtils.writeVarInt(out, sectionBlockId);
|
||||
}
|
||||
else if (bitsPerBlock <= BIT_PER_BLOCK_INDIRECTION_LIMIT) {
|
||||
} else if (bitsPerBlock <= BIT_PER_BLOCK_INDIRECTION_LIMIT) {
|
||||
int palletLength = ProtocolUtils.readVarInt(in);
|
||||
int[] pallet = ProtocolUtils.readVarIntArray(in, palletLength);
|
||||
|
||||
long[] rawData = readSectionDataFromBuffer(in, bitsPerBlock, BLOCKS_PER_SECTION);
|
||||
long[] rawData = readSectionDataFromBuffer(in, bitsPerBlock, BLOCKS_PER_SECTION);
|
||||
SimpleBitStorage data = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, rawData);
|
||||
|
||||
int[] resolvedData = new int[BLOCKS_PER_SECTION];
|
||||
for(int i = 0; i < BLOCKS_PER_SECTION; i++){
|
||||
for (int i = 0; i < BLOCKS_PER_SECTION; i++) {
|
||||
int palletReference = data.get(i);
|
||||
resolvedData[i] = pallet[palletReference];
|
||||
}
|
||||
@@ -123,7 +118,7 @@ public abstract class ChunkHider {
|
||||
|
||||
out.writeShort(blockCount);
|
||||
out.writeByte(15);
|
||||
for(long rawDataSegment : reEncodedData) {
|
||||
for (long rawDataSegment : reEncodedData) {
|
||||
out.writeLong(rawDataSegment);
|
||||
}
|
||||
/*
|
||||
@@ -156,9 +151,8 @@ public abstract class ChunkHider {
|
||||
out.writeLong(rawDataSegment);
|
||||
}*/
|
||||
|
||||
}
|
||||
else {
|
||||
long[] rawData = readSectionDataFromBuffer(in, bitsPerBlock, BLOCKS_PER_SECTION);
|
||||
} else {
|
||||
long[] rawData = readSectionDataFromBuffer(in, bitsPerBlock, BLOCKS_PER_SECTION);
|
||||
|
||||
int[] blockData = resolveDirectBlockDataArray(rawData, bitsPerBlock);
|
||||
|
||||
@@ -169,7 +163,7 @@ public abstract class ChunkHider {
|
||||
|
||||
out.writeShort(blockCount);
|
||||
out.writeByte(15);
|
||||
for(long rawDataSegment : reEncodedData) {
|
||||
for (long rawDataSegment : reEncodedData) {
|
||||
out.writeLong(rawDataSegment);
|
||||
}
|
||||
}
|
||||
@@ -185,7 +179,7 @@ public abstract class ChunkHider {
|
||||
out.readBytes(data);
|
||||
|
||||
List<Object> blockEntities = chunkBlockEntitiesDataField.get(chunkData);
|
||||
List<Object> filteredBlockEntities = filterBlockEntities(player, blockEntities);
|
||||
List<Object> filteredBlockEntities = filterBlockEntities(player, blockEntities, chunkX, chunkZ);
|
||||
|
||||
return buildNewChunkPacket(packet, data, filteredBlockEntities);
|
||||
|
||||
@@ -203,15 +197,13 @@ public abstract class ChunkHider {
|
||||
int worldZ = sectionZ + (SECTION_SPAN_SIZE * chunkZ);
|
||||
|
||||
int blockId = blockDataArray[blockDataIndex];
|
||||
BlockState blockState = Block.BLOCK_STATE_REGISTRY.byId(blockId);
|
||||
BlockState blockState = Block.BLOCK_STATE_REGISTRY.byId(blockId);
|
||||
Block block = blockState.getBlock();
|
||||
|
||||
|
||||
|
||||
if(isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block)) {
|
||||
if (accessPrivilegeProvider.isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlock(player, worldX, worldY, worldZ, block) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlockState(player, worldX, worldY, worldZ, blockState)) {
|
||||
obfuscatedData[blockDataIndex] = blockId;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
obfuscatedData[blockDataIndex] = blockIdUsedForHiding;
|
||||
}
|
||||
}
|
||||
@@ -225,7 +217,7 @@ public abstract class ChunkHider {
|
||||
SimpleBitStorage data = new SimpleBitStorage(bitsPerBlock, BLOCKS_PER_SECTION, rawDataArray);
|
||||
|
||||
int[] resolvedData = new int[BLOCKS_PER_SECTION];
|
||||
for(int i = 0; i < BLOCKS_PER_SECTION; i++){
|
||||
for (int i = 0; i < BLOCKS_PER_SECTION; i++) {
|
||||
resolvedData[i] = data.get(i);
|
||||
}
|
||||
|
||||
@@ -236,7 +228,7 @@ public abstract class ChunkHider {
|
||||
int longsRequiredToEncodeData = getLongsRequiredToEncodeEntries(15, blockDataArray.length);
|
||||
|
||||
SimpleBitStorage reEncodedData = new SimpleBitStorage(15, BLOCKS_PER_SECTION, new long[longsRequiredToEncodeData]);
|
||||
for(int i = 0; i < blockDataArray.length; i++) {
|
||||
for (int i = 0; i < blockDataArray.length; i++) {
|
||||
int blockId = blockDataArray[i];
|
||||
|
||||
reEncodedData.set(i, blockId);
|
||||
@@ -245,10 +237,6 @@ public abstract class ChunkHider {
|
||||
return reEncodedData.getRaw();
|
||||
}
|
||||
|
||||
public abstract boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ);
|
||||
public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block);
|
||||
public abstract boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType<?> type);
|
||||
|
||||
private ClientboundLevelChunkWithLightPacket buildNewChunkPacket(ClientboundLevelChunkWithLightPacket originalPacket, byte[] newBlockDataBuffer, List<Object> newBlockEntities) {
|
||||
ClientboundLevelChunkWithLightPacket clonedPacket = (ClientboundLevelChunkWithLightPacket) chunkPacketShallowCloner.apply(originalPacket);
|
||||
ClientboundLevelChunkPacketData clonedPacketChunkData = (ClientboundLevelChunkPacketData) chunkDataShallowCloner.apply(originalPacket.getChunkData());
|
||||
@@ -258,7 +246,7 @@ public abstract class ChunkHider {
|
||||
|
||||
levelChunkPacketDataField.set(clonedPacket, clonedPacketChunkData);
|
||||
|
||||
return clonedPacket;
|
||||
return clonedPacket;
|
||||
}
|
||||
|
||||
|
||||
@@ -267,48 +255,52 @@ public abstract class ChunkHider {
|
||||
private static final Reflection.Field<BlockEntityType> blockEntityInfoTypeField = Reflection.getField(blockEntitiyInfoClass, BlockEntityType.class, 0);
|
||||
private static final Reflection.Field<Integer> packedXZField = Reflection.getField(blockEntitiyInfoClass, int.class, 0);
|
||||
private static final Reflection.Field<Integer> yField = Reflection.getField(blockEntitiyInfoClass, int.class, 1);
|
||||
private List<Object> filterBlockEntities(Player player, List<Object> blockEntities) {
|
||||
|
||||
private List<Object> filterBlockEntities(Player player, List<Object> blockEntities, int chunkX, int chunkZ) {
|
||||
int fourBitBitmask = 0b0000_1111;
|
||||
|
||||
return blockEntities.stream()
|
||||
.filter((blockEntityInfo) -> {
|
||||
BlockEntityType<?> type = blockEntityInfoTypeField.get(blockEntityInfo);
|
||||
|
||||
int packedXZ = packedXZField.get(blockEntityInfo);
|
||||
|
||||
int y = yField.get(blockEntityInfo);
|
||||
int x = SectionPos.sectionRelativeX((short) packedXZ);
|
||||
int z = SectionPos.sectionRelativeZ((short) packedXZ);
|
||||
int localX = (packedXZ >> 4) & fourBitBitmask;
|
||||
int localZ = packedXZ & fourBitBitmask;
|
||||
|
||||
return isPlayerPrivilegedToAccessPosition(player, x, y, z) && isPlayerPrivilegedToAccessBlockEntity(player, x, y, z, type);
|
||||
int worldX = (chunkX * SECTION_SPAN_SIZE) + localX;
|
||||
int worldZ = (chunkZ * SECTION_SPAN_SIZE) + localZ;
|
||||
|
||||
int worldY = yField.get(blockEntityInfo);
|
||||
|
||||
return accessPrivilegeProvider.isPlayerPrivilegedToAccessPosition(player, worldX, worldY, worldZ) && accessPrivilegeProvider.isPlayerPrivilegedToAccessBlockEntity(player, worldX, worldY, worldZ, type);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private void copyOverSectionBiomeData(ByteBuf oldData, ByteBuf newData){
|
||||
private void copyOverSectionBiomeData(ByteBuf oldData, ByteBuf newData) {
|
||||
short bitsPerBiome = oldData.readByte();
|
||||
newData.writeByte(bitsPerBiome);
|
||||
if(bitsPerBiome == 0) {
|
||||
if (bitsPerBiome == 0) {
|
||||
int sectionBiomeId = ProtocolUtils.readVarInt(oldData);
|
||||
ProtocolUtils.writeVarInt(newData, sectionBiomeId);
|
||||
}
|
||||
else if(bitsPerBiome <= BIT_PER_BIOME_INDIRECTION_LIMIT) {
|
||||
} else if (bitsPerBiome <= BIT_PER_BIOME_INDIRECTION_LIMIT) {
|
||||
int palletLength = ProtocolUtils.readVarInt(oldData);
|
||||
ProtocolUtils.writeVarInt(newData, palletLength);
|
||||
|
||||
for(int i = 0; i < palletLength; i++) {
|
||||
for (int i = 0; i < palletLength; i++) {
|
||||
int palletEntry = ProtocolUtils.readVarInt(oldData);
|
||||
ProtocolUtils.writeVarInt(newData, palletEntry);
|
||||
}
|
||||
|
||||
long[] rawData = readSectionDataFromBuffer(oldData, bitsPerBiome, BIOMES_PER_SECTION);
|
||||
for(long rawDataSegment : rawData ) {
|
||||
long[] rawData = readSectionDataFromBuffer(oldData, bitsPerBiome, BIOMES_PER_SECTION);
|
||||
for (long rawDataSegment : rawData) {
|
||||
newData.writeLong(rawDataSegment);
|
||||
}
|
||||
} else {
|
||||
long[] rawData = readSectionDataFromBuffer(oldData, bitsPerBiome, BIOMES_PER_SECTION);
|
||||
for (long rawDataSegment : rawData) {
|
||||
newData.writeLong(rawDataSegment);
|
||||
}
|
||||
}
|
||||
else {
|
||||
long[] rawData = readSectionDataFromBuffer(oldData, bitsPerBiome, BIOMES_PER_SECTION);
|
||||
for(long rawDataSegment : rawData ) {
|
||||
newData.writeLong(rawDataSegment);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ public class ProtocolUtils {
|
||||
public static int[] readVarIntArray(ByteBuf buffer, int length) {
|
||||
int[] array = new int[length];
|
||||
|
||||
for(int i = 0; i < length; i++) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
array[i] = readVarInt(buffer);
|
||||
}
|
||||
|
||||
@@ -162,8 +162,8 @@ public class ProtocolUtils {
|
||||
} while (value != 0);
|
||||
}
|
||||
|
||||
public static void writeVarIntArray(ByteBuf buf, int[] values){
|
||||
for(int varInt : values) {
|
||||
public static void writeVarIntArray(ByteBuf buf, int[] values) {
|
||||
for (int varInt : values) {
|
||||
writeVarInt(buf, varInt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
/*
|
||||
* This file is a part of the SteamWar software.
|
||||
*
|
||||
* Copyright (C) 2026 SteamWar.de-Serverteam
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.steamwar.techhider;
|
||||
|
||||
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||
@@ -6,11 +25,6 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortArraySet;
|
||||
import it.unimi.dsi.fastutil.shorts.ShortSets;
|
||||
import net.minecraft.network.protocol.game.*;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.entity.Player;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.network.PacketListener;
|
||||
@@ -23,13 +37,11 @@ import net.minecraft.network.protocol.configuration.ClientboundUpdateEnabledFeat
|
||||
import net.minecraft.network.protocol.cookie.ClientboundCookieRequestPacket;
|
||||
import net.minecraft.network.protocol.game.*;
|
||||
import net.minecraft.network.protocol.login.*;
|
||||
import net.minecraft.network.protocol.login.ClientboundCustomQueryPacket;
|
||||
import net.minecraft.network.protocol.login.ClientboundHelloPacket;
|
||||
import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket;
|
||||
import net.minecraft.network.protocol.login.ClientboundLoginDisconnectPacket;
|
||||
import net.minecraft.network.protocol.login.ClientboundLoginFinishedPacket;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
@@ -41,35 +53,21 @@ import java.util.function.ToIntFunction;
|
||||
* any packet must be explicitly whitelisted or processed in a
|
||||
* way that is also compliant with the principle of default-deny.
|
||||
*/
|
||||
public abstract class TechHider {
|
||||
public class TechHider {
|
||||
private final Set<Class<?>> bypassingPackets;
|
||||
private final Map<Class<? extends Packet<? extends PacketListener>>, BiFunction<Player, Packet<? extends PacketListener>, Packet<? extends PacketListener>>> packetProcessors;
|
||||
|
||||
private final Block blockUsedForObfuscation;
|
||||
private final BlockState blockStateUsedForObfuscation;
|
||||
private ChunkHider chunkHider;
|
||||
private final ChunkHider chunkHider;
|
||||
|
||||
private final AccessPrivilegeProvider privilegeProvider;
|
||||
|
||||
// TODO handle packet bundle
|
||||
public TechHider(Block blockUsedForObfuscation) {
|
||||
this.blockUsedForObfuscation = blockUsedForObfuscation;
|
||||
public TechHider(Block blockUsedForObfuscation, AccessPrivilegeProvider privilegeProvider) {
|
||||
this.blockStateUsedForObfuscation = blockUsedForObfuscation.defaultBlockState();
|
||||
|
||||
this.chunkHider = new ChunkHider(blockUsedForObfuscation) {
|
||||
@Override
|
||||
public boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block) {
|
||||
return TechHider.this.isPlayerPrivilegedToAccessBlock(p, blockX, blockY, blockZ, block);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayerPrivilegedToAccessBlockEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType<?> type) {
|
||||
return TechHider.this.isPlayerPrivilegedToAccessBlocEntity(p, blockX, blockY, blockZ, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ) {
|
||||
return TechHider.this.isPlayerPrivilegedToAccessPosition(p, blockX, blockY, blockZ);
|
||||
}
|
||||
};
|
||||
this.chunkHider = new ChunkHider(blockUsedForObfuscation, privilegeProvider);
|
||||
this.privilegeProvider = privilegeProvider;
|
||||
|
||||
this.bypassingPackets = new HashSet<>(List.of(
|
||||
// --- 5.1.x Login Protocol ---
|
||||
@@ -81,8 +79,7 @@ public abstract class TechHider {
|
||||
ClientboundCookieRequestPacket.class, // 5.1.6 Cookie Request
|
||||
|
||||
// --- 6.1.x Configuration Protocol ---
|
||||
ClientboundSelectKnownPacks.class,
|
||||
ClientboundCustomPayloadPacket.class, // 6.1.2 Clientbound Plugin Message
|
||||
ClientboundSelectKnownPacks.class, ClientboundCustomPayloadPacket.class, // 6.1.2 Clientbound Plugin Message
|
||||
ClientboundFinishConfigurationPacket.class, // 6.1.4 Finish Configuration
|
||||
ClientboundKeepAlivePacket.class, // 6.1.5 Clientbound Keep Alive
|
||||
ClientboundPingPacket.class, // 6.1.6 Ping
|
||||
@@ -95,7 +92,7 @@ public abstract class TechHider {
|
||||
ClientboundCustomReportDetailsPacket.class, // 6.1.16 Custom Report Details
|
||||
ClientboundServerLinksPacket.class, // 6.1.17 Server Links
|
||||
ClientboundSystemChatPacket.class, // 6.1.18/19 Dialogs are often handled via System Chat or Custom
|
||||
// Payloads
|
||||
// Payloads
|
||||
ClientboundServerDataPacket.class, // 6.1.20 Code of Conduct is usually in Server Data
|
||||
|
||||
// --- 7.1.x Play Protocol ---
|
||||
@@ -190,6 +187,8 @@ public abstract class TechHider {
|
||||
ClientboundMoveVehiclePacket.class, // 7.1.56 Move Vehicle (vehicle the player is in)
|
||||
ClientboundStopSoundPacket.class, // 7.1.118 Stop Sound: sound state side channel
|
||||
|
||||
ClientboundLightUpdatePacket.class, // 7.1.48 Update Light
|
||||
|
||||
ClientboundContainerSetContentPacket.class, // 7.1.19 Set Container Content
|
||||
ClientboundContainerSetDataPacket.class, // 7.1.20 Set Container Property
|
||||
ClientboundContainerSetSlotPacket.class // 7.1.21 Set Container Slot
|
||||
@@ -232,7 +231,7 @@ public abstract class TechHider {
|
||||
// 7.1.134 Projectile Power: projectile/entity signal.
|
||||
processors.put(ClientboundProjectilePowerPacket.class, this.buildEntityPacketProcessor(ClientboundProjectilePowerPacket::getId));
|
||||
// 7.1.123 Pickup Item: item/entity ids and pickup activity.
|
||||
processors.put(ClientboundTakeItemEntityPacket.class, this.buildEntityPacketProcessor(ClientboundTakeItemEntityPacket::getItemId));
|
||||
processors.put(ClientboundTakeItemEntityPacket.class, this.buildEntityPacketProcessor(ClientboundTakeItemEntityPacket::getItemId));
|
||||
// 7.1.67 Combat Death: entity/player ids and death message context.
|
||||
processors.put(ClientboundPlayerCombatKillPacket.class, this.buildEntityPacketProcessor(ClientboundPlayerCombatKillPacket::playerId));
|
||||
// 7.1.52/53/55 Update Entity Position/Rotation: entity id and movement signal.
|
||||
@@ -274,10 +273,6 @@ public abstract class TechHider {
|
||||
// 7.1.47 Particle: particle type and position can reveal hidden machinery.
|
||||
processors.put(ClientboundLevelParticlesPacket.class, (p, packet) -> processLevelParticlesPacket(p, (ClientboundLevelParticlesPacket) packet));
|
||||
|
||||
// --- Lighting packets ---
|
||||
// 7.1.48 Update Light: lighting can reveal hidden blocks/operations.
|
||||
processors.put(ClientboundLightUpdatePacket.class, tossPacket);
|
||||
|
||||
// --- Sound packets ---
|
||||
// 7.1.115 Entity Sound Effect: entity id and sound.
|
||||
processors.put(ClientboundSoundEntityPacket.class, this.buildEntityPacketProcessor(ClientboundSoundEntityPacket::getId));
|
||||
@@ -304,48 +299,50 @@ public abstract class TechHider {
|
||||
int blockZ = (int) pos.z;
|
||||
|
||||
|
||||
return proccessPositionBasedPacket(p, blockX, blockY, blockZ, packet);
|
||||
return proccessPositionBasedPacket(p, blockX, blockY, blockZ, packet);
|
||||
});
|
||||
|
||||
this.packetProcessors = processors;
|
||||
|
||||
TinyProtocol.instance.addGlobalClientboundFilter((player, rawPacket) -> {
|
||||
if(rawPacket instanceof ClientboundBundlePacket bundlePacket) {
|
||||
List<Packet<? super ClientGamePacketListener>> processedPackets = new ArrayList<>();
|
||||
try {
|
||||
if (rawPacket instanceof ClientboundBundlePacket bundlePacket) {
|
||||
List<Packet<? super ClientGamePacketListener>> processedPackets = new ArrayList<>();
|
||||
|
||||
for(Packet<? super ClientGamePacketListener> packet : bundlePacket.subPackets()) {
|
||||
Packet<? super ClientGamePacketListener> processedPacket = (Packet<? super ClientGamePacketListener>) processPacket(player, packet);
|
||||
for (Packet<? super ClientGamePacketListener> packet : bundlePacket.subPackets()) {
|
||||
Packet<? super ClientGamePacketListener> processedPacket = (Packet<? super ClientGamePacketListener>) processPacket(player, packet);
|
||||
|
||||
if(processedPacket != null) {
|
||||
processedPackets.add(processedPacket);
|
||||
if (processedPacket != null) {
|
||||
processedPackets.add(processedPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ClientboundBundlePacket(processedPackets);
|
||||
}
|
||||
else if(rawPacket instanceof Packet) {
|
||||
return processPacket(player, (Packet<?>) rawPacket);
|
||||
}
|
||||
else {
|
||||
return new ClientboundBundlePacket(processedPackets);
|
||||
} else {
|
||||
return processPacket(player, rawPacket);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
TinyProtocol.instance.addFilter(ServerboundUseItemOnPacket.class, (p, packet) -> privilegeProvider.isPlayerPrivilegedToPerformAction(p) ? packet : null);
|
||||
TinyProtocol.instance.addFilter(ServerboundInteractPacket.class, (p, packet) -> privilegeProvider.isPlayerPrivilegedToPerformAction(p) ? packet : null);
|
||||
}
|
||||
|
||||
private Packet<?> processPacket(Player player, Packet<?> packet) {
|
||||
if(bypassingPackets.contains(packet.getClass())) {
|
||||
if (bypassingPackets.contains(packet.getClass())) {
|
||||
return packet;
|
||||
}
|
||||
else if(packetProcessors.containsKey(packet.getClass())) {
|
||||
return packetProcessors.get(packet.getClass()).apply(player, (Packet<? extends PacketListener>) packet);
|
||||
}
|
||||
else {
|
||||
} else if (packetProcessors.containsKey(packet.getClass())) {
|
||||
return packetProcessors.get(packet.getClass()).apply(player, packet);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Packet<? extends PacketListener> processAddEntityPacket(Player player, ClientboundAddEntityPacket packet) {
|
||||
if(isPlayerPrivilegedToAccessEntity(player, packet.getId()) && isPlayerPrivilegedToAccessPosition(player, (int) packet.getX(), (int) packet.getY(), (int) packet.getZ())) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, packet.getId()) && privilegeProvider.isPlayerPrivilegedToAccessPosition(player, (int) packet.getX(), (int) packet.getY(), (int) packet.getZ())) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
@@ -353,13 +350,14 @@ public abstract class TechHider {
|
||||
}
|
||||
|
||||
private Packet<? extends PacketListener> processEntityPacket(Player player, int entityId, Packet<? extends PacketListener> packet) {
|
||||
if(isPlayerPrivilegedToAccessEntity(player, entityId)) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
private <TTargetPacket extends Packet<?>> BiFunction<Player, Packet<? extends PacketListener>, Packet<? extends PacketListener>> buildEntityPacketProcessor(ToIntFunction<TTargetPacket> entityIdExtractor) {
|
||||
|
||||
private <TTargetPacket extends Packet<?>> BiFunction<Player, Packet<? extends PacketListener>, Packet<? extends PacketListener>> buildEntityPacketProcessor(ToIntFunction<TTargetPacket> entityIdExtractor) {
|
||||
return (p, rawPacket) -> {
|
||||
TTargetPacket packet = (TTargetPacket) rawPacket;
|
||||
return processEntityPacket(p, entityIdExtractor.applyAsInt(packet), packet);
|
||||
@@ -367,25 +365,25 @@ public abstract class TechHider {
|
||||
}
|
||||
|
||||
private final Reflection.Field<Integer> moveEntityPacketEntityIdField = Reflection.getField(ClientboundMoveEntityPacket.class, int.class, 0);
|
||||
|
||||
private Packet<?> processMoveEntityPacket(Player player, ClientboundMoveEntityPacket packet) {
|
||||
int entityId = moveEntityPacketEntityIdField.get(packet);
|
||||
|
||||
if(isPlayerPrivilegedToAccessEntity(player, entityId)) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) {
|
||||
return packet;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private final Reflection.Field<Integer> rotateHeadPacketEntityIdField = Reflection.getField(ClientboundRotateHeadPacket.class, int.class, 0);
|
||||
private final Reflection.Field<Integer> rotateHeadPacketEntityIdField = Reflection.getField(ClientboundRotateHeadPacket.class, int.class, 0);
|
||||
|
||||
private Packet<?> processRotateHeadPacket(Player player, ClientboundRotateHeadPacket packet) {
|
||||
int entityId = rotateHeadPacketEntityIdField.get(packet);
|
||||
|
||||
if(isPlayerPrivilegedToAccessEntity(player, entityId)) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityId)) {
|
||||
return packet;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -393,9 +391,7 @@ public abstract class TechHider {
|
||||
private Packet<?> processRemoveEntitiesPacket(Player player, ClientboundRemoveEntitiesPacket packet) {
|
||||
IntList entityIdsToRemove = packet.getEntityIds();
|
||||
|
||||
IntList filteredEntitiesToRemove = entityIdsToRemove.intStream()
|
||||
.filter((id) -> isPlayerPrivilegedToAccessEntity(player, id))
|
||||
.collect(IntArrayList::new, IntList::add, IntList::addAll);
|
||||
IntList filteredEntitiesToRemove = entityIdsToRemove.intStream().filter((id) -> privilegeProvider.isPlayerPrivilegedToAccessEntity(player, id)).collect(IntArrayList::new, IntList::add, IntList::addAll);
|
||||
|
||||
return new ClientboundRemoveEntitiesPacket(filteredEntitiesToRemove);
|
||||
}
|
||||
@@ -404,10 +400,9 @@ public abstract class TechHider {
|
||||
int fromEntityId = packet.getSourceId();
|
||||
int toEntityId = packet.getDestId();
|
||||
|
||||
if(isPlayerPrivilegedToAccessEntity(player, fromEntityId) && isPlayerPrivilegedToAccessEntity(player, toEntityId)) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, fromEntityId) && privilegeProvider.isPlayerPrivilegedToAccessEntity(player, toEntityId)) {
|
||||
return packet;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -419,7 +414,7 @@ public abstract class TechHider {
|
||||
int blockY = blockPos.getY();
|
||||
int blockZ = blockPos.getZ();
|
||||
|
||||
if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && privilegeProvider.isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
@@ -428,14 +423,15 @@ public abstract class TechHider {
|
||||
|
||||
private Packet<?> processBlockUpdatePacket(Player player, ClientboundBlockUpdatePacket packet) {
|
||||
BlockPos blockPos = packet.getPos();
|
||||
Block block = packet.getBlockState().getBlock();
|
||||
BlockState blockState = packet.getBlockState();
|
||||
Block block = blockState.getBlock();
|
||||
int blockX = blockPos.getX();
|
||||
int blockY = blockPos.getY();
|
||||
int blockZ = blockPos.getZ();
|
||||
|
||||
if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block)) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && privilegeProvider.isPlayerPrivilegedToAccessBlock(player, blockX, blockY, blockZ, block) && privilegeProvider.isPlayerPrivilegedToAccessBlockState(player, blockX, blockY, blockZ, blockState)) {
|
||||
return packet;
|
||||
} else if(isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) {
|
||||
} else if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) {
|
||||
return new ClientboundBlockUpdatePacket(blockPos, blockStateUsedForObfuscation);
|
||||
} else {
|
||||
return null;
|
||||
@@ -449,7 +445,7 @@ public abstract class TechHider {
|
||||
int blockY = blockPos.getY();
|
||||
int blockZ = blockPos.getZ();
|
||||
|
||||
if (isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && isPlayerPrivilegedToAccessBlocEntity(player, blockX, blockY, blockZ, blockEntityType)) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ) && privilegeProvider.isPlayerPrivilegedToAccessBlockEntity(player, blockX, blockY, blockZ, blockEntityType)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
@@ -460,10 +456,9 @@ public abstract class TechHider {
|
||||
BlockPos blockPos = packet.getPos();
|
||||
int entityBreakingBlockId = packet.getId();
|
||||
|
||||
if(isPlayerPrivilegedToAccessEntity(player, entityBreakingBlockId) && isPlayerPrivilegedToAccessPosition(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessEntity(player, entityBreakingBlockId) && privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockPos.getX(), blockPos.getY(), blockPos.getZ())) {
|
||||
return packet;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -471,12 +466,12 @@ public abstract class TechHider {
|
||||
private final Reflection.Field<SectionPos> sectionPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, SectionPos.class, 0);
|
||||
private final Reflection.Field<short[]> oldPosField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, short[].class, 0);
|
||||
private final Reflection.Field<BlockState[]> oldStatesField = Reflection.getField(ClientboundSectionBlocksUpdatePacket.class, BlockState[].class, 0);
|
||||
|
||||
private ClientboundSectionBlocksUpdatePacket processSectionUpdate(Player p, ClientboundSectionBlocksUpdatePacket packet) {
|
||||
SectionPos sectionPos = sectionPosField.get(packet);
|
||||
short[] oldPos = oldPosField.get(packet);
|
||||
BlockState[] oldStates = oldStatesField.get(packet);
|
||||
|
||||
boolean modified = false;
|
||||
List<Short> filteredPos = new ArrayList<>(oldPos.length);
|
||||
List<BlockState> filteredStates = new ArrayList<>(oldStates.length);
|
||||
|
||||
@@ -489,12 +484,10 @@ public abstract class TechHider {
|
||||
int worldY = sectionPos.relativeToBlockY(posShort);
|
||||
int worldZ = sectionPos.relativeToBlockZ(posShort);
|
||||
|
||||
if (isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ) && isPlayerPrivilegedToAccessBlock(p, worldX, worldY, worldZ, block)) {
|
||||
// TODO statefull !!!
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ) && privilegeProvider.isPlayerPrivilegedToAccessBlock(p, worldX, worldY, worldZ, block) && privilegeProvider.isPlayerPrivilegedToAccessBlockState(p, worldX, worldY, worldZ, state)) {
|
||||
filteredPos.add(posShort);
|
||||
filteredStates.add(state);
|
||||
} else if(isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ)){
|
||||
modified = true;
|
||||
} else if (privilegeProvider.isPlayerPrivilegedToAccessPosition(p, worldX, worldY, worldZ)) {
|
||||
filteredPos.add(posShort);
|
||||
filteredStates.add(blockStateUsedForObfuscation);
|
||||
}
|
||||
@@ -503,9 +496,6 @@ public abstract class TechHider {
|
||||
if (filteredStates.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (!modified) {
|
||||
return packet;
|
||||
}
|
||||
|
||||
short[] newPos = new short[filteredPos.size()];
|
||||
for (int i = 0; i < newPos.length; i++) {
|
||||
@@ -514,11 +504,7 @@ public abstract class TechHider {
|
||||
|
||||
BlockState[] newStates = filteredStates.toArray(new BlockState[0]);
|
||||
|
||||
return new ClientboundSectionBlocksUpdatePacket(
|
||||
sectionPos,
|
||||
ShortSets.unmodifiable(new ShortArraySet(newPos)),
|
||||
newStates
|
||||
);
|
||||
return new ClientboundSectionBlocksUpdatePacket(sectionPos, ShortSets.unmodifiable(new ShortArraySet(newPos)), newStates);
|
||||
}
|
||||
|
||||
private Packet<?> processLevelParticlesPacket(Player player, ClientboundLevelParticlesPacket packet) {
|
||||
@@ -526,30 +512,29 @@ public abstract class TechHider {
|
||||
int blockY = (int) packet.getY();
|
||||
int blockZ = (int) packet.getZ();
|
||||
|
||||
if(isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) {
|
||||
return packet;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) {
|
||||
if(isEveryonePrivilegedToAccessAllDataWithinChunk(packet.getX(), packet.getZ())) {
|
||||
return packet;
|
||||
}
|
||||
else {
|
||||
return chunkHider.processLevelChunkWithLightPacket(p, packet);
|
||||
}
|
||||
}
|
||||
|
||||
private Packet<? extends PacketListener> proccessPositionBasedPacket(Player player, int blockX, int blockY, int blockZ, Packet<? extends PacketListener> packet) {
|
||||
if(isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private ClientboundLevelChunkWithLightPacket processChunkWithLight(Player p, ClientboundLevelChunkWithLightPacket packet) {
|
||||
if (privilegeProvider.isEveryonePrivilegedToAccessAllDataWithinChunk(packet.getX(), packet.getZ())) {
|
||||
return packet;
|
||||
} else {
|
||||
return chunkHider.processLevelChunkWithLightPacket(p, packet);
|
||||
}
|
||||
}
|
||||
|
||||
private Packet<? extends PacketListener> proccessPositionBasedPacket(Player player, int blockX, int blockY, int blockZ, Packet<? extends PacketListener> packet) {
|
||||
if (privilegeProvider.isPlayerPrivilegedToAccessPosition(player, blockX, blockY, blockZ)) {
|
||||
return packet;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private <TTargetPacket extends Packet<?>> BiFunction<Player, Packet<?>, Packet<?>> buildPositionBasedPacketProcessor(Function<TTargetPacket, BlockPos> positionExtractor) {
|
||||
return (p, rawPacket) -> {
|
||||
TTargetPacket packet = (TTargetPacket) rawPacket;
|
||||
@@ -561,10 +546,4 @@ public abstract class TechHider {
|
||||
return proccessPositionBasedPacket(p, blockX, blockY, blockZ, packet);
|
||||
};
|
||||
}
|
||||
|
||||
public abstract boolean isPlayerPrivilegedToAccessPosition(Player p, int blockX, int blockY, int blockZ);
|
||||
public abstract boolean isPlayerPrivilegedToAccessBlock(Player p, int blockX, int blockY, int blockZ, Block block);
|
||||
public abstract boolean isPlayerPrivilegedToAccessEntity(Player p, int entityId);
|
||||
public abstract boolean isPlayerPrivilegedToAccessBlocEntity(Player p, int blockX, int blockY, int blockZ, BlockEntityType<?> type);
|
||||
public abstract boolean isEveryonePrivilegedToAccessAllDataWithinChunk(int chunkX, int chunkZ);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* 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.legacy;
|
||||
|
||||
import de.steamwar.Reflection;
|
||||
import de.steamwar.techhider.ProtocolUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.registries.BuiltInRegistries;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkPacketData;
|
||||
import net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.SimpleBitStorage;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Deprecated
|
||||
public class ChunkHider {
|
||||
public static final ChunkHider impl = new ChunkHider();
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
||||
private static final Registry<BlockEntityType<?>> registry = Reflection.getField(BuiltInRegistries.class, "BLOCK_ENTITY_TYPE", Registry.class).get(null);
|
||||
private static final Reflection.Method getKey = Reflection.getTypedMethod(Reflection.getClass("net.minecraft.core.Registry"), "getKey", ResourceLocation.class, Object.class);
|
||||
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);
|
||||
|
||||
protected boolean tileEntityVisible(Set<String> hiddenBlockEntities, Object tile) {
|
||||
BlockEntityType type = entityType.get(tile);
|
||||
String path = ((ResourceLocation) getKey.invoke(registry, type)).getPath();
|
||||
return !hiddenBlockEntities.contains(path);
|
||||
}
|
||||
|
||||
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 if (section.getBitsPerBlock() < 9) {
|
||||
// Indirect (paletted) storage – only present when bitsPerBlock < 9 in 1.21+
|
||||
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);
|
||||
} else {
|
||||
// Direct (global) biome IDs – no palette present
|
||||
section.skipNewDataArray(64);
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
class SectionHider {
|
||||
private final Player player;
|
||||
private final TechHider techHider;
|
||||
private final ByteBuf in;
|
||||
private final ByteBuf out;
|
||||
|
||||
private final int chunkX;
|
||||
private final int chunkY;
|
||||
private final int chunkZ;
|
||||
private final int offsetX;
|
||||
private final int offsetY;
|
||||
private final int offsetZ;
|
||||
|
||||
private final boolean skipSection;
|
||||
|
||||
private boolean paletted;
|
||||
private int bitsPerBlock;
|
||||
private int blockCount;
|
||||
private int air;
|
||||
private int target;
|
||||
private Set<Integer> obfuscate;
|
||||
|
||||
public SectionHider(Player player, TechHider techHider, ByteBuf in, ByteBuf out, int chunkX, int chunkY, int chunkZ) {
|
||||
this.player = player;
|
||||
this.techHider = techHider;
|
||||
this.in = in;
|
||||
this.out = out;
|
||||
this.chunkX = chunkX;
|
||||
this.chunkY = chunkY;
|
||||
this.chunkZ = chunkZ;
|
||||
this.offsetX = 16 * chunkX;
|
||||
this.offsetY = 16 * chunkY;
|
||||
this.offsetZ = 16 * chunkZ;
|
||||
this.skipSection = techHider.getLocationEvaluator().skipChunkSection(player, chunkX, chunkY, chunkZ);
|
||||
|
||||
this.paletted = false;
|
||||
this.bitsPerBlock = 0;
|
||||
this.air = TechHider.AIR_ID;
|
||||
this.target = techHider.getObfuscationTargetId();
|
||||
this.obfuscate = techHider.getObfuscateIds();
|
||||
}
|
||||
|
||||
public boolean blockPrecise() {
|
||||
return techHider.getLocationEvaluator().blockPrecise(player, chunkX, chunkY, chunkZ);
|
||||
}
|
||||
|
||||
public TechHider.State test(int x, int y, int z) {
|
||||
return techHider.getLocationEvaluator().check(player, offsetX + x, offsetY + y, offsetZ + z);
|
||||
}
|
||||
|
||||
public void copyBlockCount() {
|
||||
this.blockCount = in.readShort();
|
||||
out.writeShort(blockCount);
|
||||
}
|
||||
|
||||
public void copyBitsPerBlock() {
|
||||
bitsPerBlock = in.readByte();
|
||||
out.writeByte(bitsPerBlock);
|
||||
}
|
||||
|
||||
public int copyVarInt() {
|
||||
int value = ProtocolUtils.readVarInt(in);
|
||||
ProtocolUtils.writeVarInt(out, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
public void skipPalette() {
|
||||
int paletteLength = copyVarInt();
|
||||
for (int i = 0; i < paletteLength; i++) {
|
||||
copyVarInt();
|
||||
}
|
||||
}
|
||||
|
||||
public void processPalette() {
|
||||
if (skipSection) {
|
||||
skipPalette();
|
||||
return;
|
||||
}
|
||||
|
||||
int paletteLength = copyVarInt();
|
||||
if (paletteLength == 0) return;
|
||||
|
||||
paletted = true;
|
||||
air = 0;
|
||||
target = 0;
|
||||
|
||||
for (int i = 0; i < paletteLength; i++) {
|
||||
int entry = ProtocolUtils.readVarInt(in);
|
||||
if (obfuscate.contains(entry)) {
|
||||
entry = techHider.getObfuscationTargetId();
|
||||
}
|
||||
|
||||
if (entry == TechHider.AIR_ID) {
|
||||
air = i;
|
||||
} else if (entry == techHider.getObfuscationTargetId()) {
|
||||
target = i;
|
||||
}
|
||||
|
||||
ProtocolUtils.writeVarInt(out, entry);
|
||||
}
|
||||
obfuscate = Collections.emptySet();
|
||||
}
|
||||
|
||||
public void skipDataArray() {
|
||||
int dataArrayLength = copyVarInt();
|
||||
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++) {
|
||||
array[i] = in.readLong();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.legacy;
|
||||
|
||||
import de.steamwar.Reflection;
|
||||
import net.minecraft.core.SectionPos;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
@Deprecated
|
||||
public class ProtocolWrapper {
|
||||
public static final ProtocolWrapper impl = new ProtocolWrapper();
|
||||
|
||||
private static final Reflection.Field<SectionPos> multiBlockChangeChunk = Reflection.getField(TechHider.multiBlockChangePacket, SectionPos.class, 0);
|
||||
private static final Reflection.Field<short[]> multiBlockChangePos = Reflection.getField(TechHider.multiBlockChangePacket, short[].class, 0);
|
||||
private static final Reflection.Field<BlockState[]> multiBlockChangeBlocks = Reflection.getField(TechHider.multiBlockChangePacket, BlockState[].class, 0);
|
||||
|
||||
public BiFunction<Player, Object, Object> multiBlockChangeGenerator(TechHider techHider) {
|
||||
return (p, packet) -> {
|
||||
TechHider.LocationEvaluator locationEvaluator = techHider.getLocationEvaluator();
|
||||
Object chunkCoords = multiBlockChangeChunk.get(packet);
|
||||
int chunkX = TechHider.blockPositionX.get(chunkCoords);
|
||||
int chunkY = TechHider.blockPositionY.get(chunkCoords);
|
||||
int chunkZ = TechHider.blockPositionZ.get(chunkCoords);
|
||||
if (locationEvaluator.skipChunkSection(p, chunkX, chunkY, chunkZ)) {
|
||||
return packet;
|
||||
}
|
||||
|
||||
packet = TechHider.multiBlockChangeCloner.apply(packet);
|
||||
final short[] oldPos = multiBlockChangePos.get(packet);
|
||||
final BlockState[] oldBlocks = multiBlockChangeBlocks.get(packet);
|
||||
ArrayList<Short> poss = new ArrayList<>(oldPos.length);
|
||||
ArrayList<BlockState> blocks = new ArrayList<>(oldPos.length);
|
||||
for (int i = 0; i < oldPos.length; i++) {
|
||||
short pos = oldPos[i];
|
||||
BlockState block = oldBlocks[i];
|
||||
switch (locationEvaluator.check(p, 16 * chunkX + (pos >> 8 & 0xF), 16 * chunkY + (pos & 0xF), 16 * chunkZ + (pos >> 4 & 0xF))) {
|
||||
case SKIP:
|
||||
poss.add(pos);
|
||||
blocks.add(block);
|
||||
break;
|
||||
case CHECK:
|
||||
poss.add(pos);
|
||||
blocks.add(techHider.iBlockDataHidden(block) ? (BlockState) techHider.getObfuscationTarget() : block);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (blocks.isEmpty()) return null;
|
||||
|
||||
short[] newPos = new short[poss.size()];
|
||||
for (int i = 0; i < newPos.length; i++) {
|
||||
newPos[i] = poss.get(i);
|
||||
}
|
||||
|
||||
multiBlockChangePos.set(packet, newPos);
|
||||
multiBlockChangeBlocks.set(packet, blocks.toArray(new BlockState[0]));
|
||||
return packet;
|
||||
};
|
||||
}
|
||||
|
||||
private static final Reflection.Field<BlockEntityType> tileEntityType = Reflection.getField(TechHider.tileEntityDataPacket, BlockEntityType.class, 0);
|
||||
private static final BlockEntityType<?> signType = Reflection.getField(BlockEntityType.class, BlockEntityType.class, 0, SignBlockEntity.class).get(null);
|
||||
|
||||
public boolean unfilteredTileEntityDataAction(Object packet) {
|
||||
return tileEntityType.get(packet) != signType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* 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.legacy;
|
||||
|
||||
import com.comphenix.tinyprotocol.TinyProtocol;
|
||||
import de.steamwar.Reflection;
|
||||
import de.steamwar.techhider.BlockIds;
|
||||
import de.steamwar.techhider.ProtocolUtils;
|
||||
import lombok.Getter;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.network.protocol.game.*;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.craftbukkit.util.CraftMagicNumbers;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Deprecated
|
||||
public class TechHider {
|
||||
|
||||
public static final Class<?> blockPosition = BlockPos.class;
|
||||
private static final Class<?> baseBlockPosition = Vec3i.class;
|
||||
public static final Reflection.Field<Integer> blockPositionX = Reflection.getField(baseBlockPosition, int.class, 0);
|
||||
public static final Reflection.Field<Integer> blockPositionY = Reflection.getField(baseBlockPosition, int.class, 1);
|
||||
public static final Reflection.Field<Integer> blockPositionZ = Reflection.getField(baseBlockPosition, int.class, 2);
|
||||
|
||||
public static final Class<?> iBlockData = BlockState.class;
|
||||
public static final Class<?> block = Block.class;
|
||||
|
||||
public boolean iBlockDataHidden(BlockState iBlockData) {
|
||||
return obfuscateIds.contains(BlockIds.impl.getCombinedId(iBlockData));
|
||||
}
|
||||
|
||||
public static final Object AIR = CraftMagicNumbers.getBlock(Material.AIR).defaultBlockState();
|
||||
public static final int AIR_ID = BlockIds.impl.materialToId(Material.AIR);
|
||||
|
||||
private final Map<Class<?>, BiFunction<Player, Object, Object>> techhiders = new HashMap<>();
|
||||
@Getter
|
||||
private final LocationEvaluator locationEvaluator;
|
||||
@Getter
|
||||
private final Object obfuscationTarget;
|
||||
@Getter
|
||||
private final int obfuscationTargetId;
|
||||
@Getter
|
||||
private final Set<Integer> obfuscateIds;
|
||||
@Getter
|
||||
private final Set<String> hiddenBlockEntities;
|
||||
|
||||
public TechHider(LocationEvaluator locationEvaluator, Material obfuscationTarget, Set<Material> obfuscate, Set<String> hiddenBlockEntities) {
|
||||
this.locationEvaluator = locationEvaluator;
|
||||
this.obfuscateIds = obfuscate.stream().flatMap(m -> BlockIds.impl.materialToAllIds(m).stream()).collect(Collectors.toSet());
|
||||
this.hiddenBlockEntities = hiddenBlockEntities;
|
||||
this.obfuscationTarget = CraftMagicNumbers.getBlock(obfuscationTarget).defaultBlockState();
|
||||
this.obfuscationTargetId = BlockIds.impl.materialToId(obfuscationTarget);
|
||||
|
||||
techhiders.put(blockActionPacket, this::blockActionHider);
|
||||
techhiders.put(blockChangePacket, this::blockChangeHider);
|
||||
techhiders.put(tileEntityDataPacket, this::tileEntityDataHider);
|
||||
techhiders.put(multiBlockChangePacket, ProtocolWrapper.impl.multiBlockChangeGenerator(this));
|
||||
techhiders.put(ChunkHider.impl.mapChunkPacket(), ChunkHider.impl.chunkHiderGenerator(this));
|
||||
techhiders.put(ServerboundUseItemOnPacket.class, (p, packet) -> locationEvaluator.suppressInteractions(p) ? null : packet);
|
||||
techhiders.put(ServerboundInteractPacket.class, (p, packet) -> locationEvaluator.suppressInteractions(p) ? null : packet);
|
||||
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
techhiders.forEach((type, playerObjectBiFunction) -> TinyProtocol.instance.addFilter((Class) type, playerObjectBiFunction));
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
techhiders.forEach(TinyProtocol.instance::removeFilter);
|
||||
}
|
||||
|
||||
public static final Class<?> multiBlockChangePacket = ClientboundSectionBlocksUpdatePacket.class;
|
||||
public static final UnaryOperator<Object> multiBlockChangeCloner = ProtocolUtils.shallowCloneGenerator(TechHider.multiBlockChangePacket);
|
||||
|
||||
private static final Class<?> blockChangePacket = ClientboundBlockUpdatePacket.class;
|
||||
private static final Function<Object, Object> blockChangeCloner = ProtocolUtils.shallowCloneGenerator(blockChangePacket);
|
||||
private static final Reflection.Field<?> blockChangePosition = Reflection.getField(blockChangePacket, blockPosition, 0);
|
||||
private static final Reflection.Field<?> blockChangeBlockData = Reflection.getField(blockChangePacket, iBlockData, 0);
|
||||
|
||||
private Object blockChangeHider(Player p, Object packet) {
|
||||
switch (locationEvaluator.checkBlockPos(p, blockChangePosition.get(packet))) {
|
||||
case SKIP:
|
||||
return packet;
|
||||
case CHECK:
|
||||
if (!iBlockDataHidden((BlockState) blockChangeBlockData.get(packet))) {
|
||||
return packet;
|
||||
}
|
||||
case HIDE:
|
||||
packet = blockChangeCloner.apply(packet);
|
||||
blockChangeBlockData.set(packet, obfuscationTarget);
|
||||
return packet;
|
||||
case HIDE_AIR:
|
||||
default:
|
||||
packet = blockChangeCloner.apply(packet);
|
||||
blockChangeBlockData.set(packet, AIR);
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Class<?> blockActionPacket = ClientboundBlockEventPacket.class;
|
||||
private static final Reflection.Field<?> blockActionPosition = Reflection.getField(blockActionPacket, blockPosition, 0);
|
||||
|
||||
private Object blockActionHider(Player p, Object packet) {
|
||||
if (locationEvaluator.checkBlockPos(p, blockActionPosition.get(packet)) == State.SKIP) {
|
||||
return packet;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static final Class<?> tileEntityDataPacket = ClientboundBlockEntityDataPacket.class;
|
||||
private static final Reflection.Field<?> tileEntityDataPosition = Reflection.getField(tileEntityDataPacket, blockPosition, 0);
|
||||
|
||||
private Object tileEntityDataHider(Player p, Object packet) {
|
||||
switch (locationEvaluator.checkBlockPos(p, tileEntityDataPosition.get(packet))) {
|
||||
case SKIP:
|
||||
return packet;
|
||||
case CHECK:
|
||||
if (ProtocolWrapper.impl.unfilteredTileEntityDataAction(packet)) {
|
||||
return packet;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum State {
|
||||
SKIP,
|
||||
CHECK,
|
||||
HIDE,
|
||||
HIDE_AIR
|
||||
}
|
||||
|
||||
public interface LocationEvaluator {
|
||||
default boolean suppressInteractions(Player player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean skipChunk(Player player, int x, int z);
|
||||
|
||||
default boolean skipChunkSection(Player player, int x, int y, int z) {
|
||||
return skipChunk(player, x, z);
|
||||
}
|
||||
|
||||
default State check(Player player, int x, int y, int z) {
|
||||
return skipChunkSection(player, ProtocolUtils.posToChunk(x), ProtocolUtils.posToChunk(y), ProtocolUtils.posToChunk(z)) ? State.SKIP : State.CHECK;
|
||||
}
|
||||
|
||||
default State checkBlockPos(Player player, Object pos) {
|
||||
return check(player, blockPositionX.get(pos), blockPositionY.get(pos), blockPositionZ.get(pos));
|
||||
}
|
||||
|
||||
default boolean blockPrecise(Player player, int x, int y, int z) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,9 @@ import net.md_5.bungee.api.chat.ClickEvent
|
||||
import org.bukkit.entity.Player
|
||||
|
||||
object InviteCommand : SWCommand("invite") {
|
||||
init {
|
||||
message = de.steamwar.tntleague.message
|
||||
}
|
||||
|
||||
@Register
|
||||
fun invitePlayer(@Validator("isLeader") sender: Player, target: Player) {
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.util.function.Consumer;
|
||||
@Getter
|
||||
public class Bauserver extends Subserver {
|
||||
private static final Map<UUID, Bauserver> servers = new HashMap<>();
|
||||
private static final Map<UUID, Bauserver> serversByWorld = new HashMap<>();
|
||||
|
||||
public static Bauserver get(UUID owner) {
|
||||
synchronized (servers) {
|
||||
@@ -36,25 +37,43 @@ public class Bauserver extends Subserver {
|
||||
}
|
||||
}
|
||||
|
||||
public static Bauserver getByWorld(UUID world) {
|
||||
synchronized (serversByWorld) {
|
||||
return serversByWorld.get(world);
|
||||
}
|
||||
}
|
||||
|
||||
private final UUID owner;
|
||||
private final UUID world;
|
||||
|
||||
public Bauserver(String serverName, UUID owner, int port, ProcessBuilder processBuilder, Runnable shutdownCallback) {
|
||||
this(serverName, owner, port, processBuilder, shutdownCallback, null);
|
||||
this(serverName, owner, owner, port, processBuilder, shutdownCallback, null);
|
||||
}
|
||||
|
||||
public Bauserver(String serverName, UUID owner, int port, ProcessBuilder processBuilder, Runnable shutdownCallback, Consumer<Exception> failureCallback) {
|
||||
this(serverName, owner, owner, port, processBuilder, shutdownCallback, failureCallback);
|
||||
}
|
||||
|
||||
public Bauserver(String serverName, UUID owner, UUID world, int port, ProcessBuilder processBuilder, Runnable shutdownCallback, Consumer<Exception> failureCallback) {
|
||||
super(serverName, port, processBuilder, shutdownCallback, failureCallback);
|
||||
this.owner = owner;
|
||||
this.world = world;
|
||||
|
||||
synchronized (servers) {
|
||||
servers.put(owner, this);
|
||||
servers.putIfAbsent(owner, this);
|
||||
}
|
||||
synchronized (serversByWorld) {
|
||||
serversByWorld.put(world, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void unregister() {
|
||||
synchronized (servers) {
|
||||
servers.remove(owner);
|
||||
servers.remove(owner, this);
|
||||
}
|
||||
synchronized (serversByWorld) {
|
||||
serversByWorld.remove(world);
|
||||
}
|
||||
super.unregister();
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ if __name__ == "__main__":
|
||||
with open(configfile, 'r') as file:
|
||||
gamemode = yaml.load(file)
|
||||
|
||||
builderworld = path.expanduser(f'/worlds/builder{version}/{worldname}')
|
||||
arenaworld = f'/servers/{gamemode["Server"]["Folder"]}/arenas/{worldname}'
|
||||
builderworld = sys.argv[4] if len(sys.argv) > 4 else path.expanduser(f'/worlds/builder{version}/{worldname}')
|
||||
arenaworld = sys.argv[5] if len(sys.argv) > 5 else f'/servers/{gamemode["Server"]["Folder"]}/arenas/{worldname}'
|
||||
|
||||
if path.exists(arenaworld):
|
||||
backupworld = path.expanduser(f'/mnt/storage/backup/arenas/{datetime.datetime.now()}-{worldname}-{version}.tar.xz')
|
||||
@@ -91,7 +91,7 @@ if __name__ == "__main__":
|
||||
if path.exists(arenaworld):
|
||||
shutil.rmtree(arenaworld)
|
||||
|
||||
os.makedirs(f'{arenaworld}/backup')
|
||||
os.makedirs(f'{arenaworld}/backup', exist_ok=True)
|
||||
shutil.copy2(f'{builderworld}/level.dat', f'{arenaworld}/backup/level.dat')
|
||||
shutil.copytree(f'{builderworld}/region', f'{arenaworld}/backup/region')
|
||||
|
||||
|
||||
@@ -219,22 +219,22 @@ IGNORE_ALREADY = §cYou are already ignoring this player.
|
||||
IGNORE_MESSAGE = §7You are now ignoring §e{0}§8.
|
||||
|
||||
#BauCommand
|
||||
BAU_ADDMEMBER_USAGE = §8/§7build addmember §8[§eplayer§8]
|
||||
BAU_ADDMEMBER_USAGE = §8/§7build addmember §8[§eworld/all§8] §8[§eplayer§8]
|
||||
BAU_ADDMEMBER_SELFADD = §cYou don't have to add yourself!
|
||||
BAU_ADDMEMBER_ISADDED = §cThis player is already a member of your world.
|
||||
BAU_ADDMEMBER_ADDED = §aThe player was added to your world.
|
||||
BAU_ADDMEMBER_ADDED_TARGET = §aYou have been added to the world of §e{0}§a.
|
||||
BAU_TP_USAGE = §8/§7build tp §8[§eplayer§8]
|
||||
BAU_TP_USAGE = §8/§7build tp §8[§eplayer§8] §8<§eworld§8>
|
||||
BAU_TP_NOALLOWED = §cYou are not allowed to teleport to this player's world.
|
||||
BAU_LOCKED_NOALLOWED = §cThe build server is currently locked.
|
||||
BAU_LOCK_BLOCKED = §cYour build lock has prevented §e{0} §cfrom joining.
|
||||
BAU_LOCKED_OPTIONS = §7Build server lock options§8: §cnobody§8, §eserverteam§8, §eteam_and_serverteam§8, §eteam§8, §aopen
|
||||
BAU_LOCKED_OPTIONS = §8/§7build lock §8[§eworld§8] §8[§eoption§8] §7Options§8: §cnobody§8, §eserverteam§8, §eteam_and_serverteam§8, §eteam§8, §aopen
|
||||
BAU_LOCKED_NOBODY = §7You have locked your build server for all players.
|
||||
BAU_LOCKED_SERVERTEAM = §7You have locked your build server for all players except added server team members.
|
||||
BAU_LOCKED_TEAM_AND_SERVERTEAM = §7You have locked your build server for all players except added team members and server team members.
|
||||
BAU_LOCKED_TEAM = §7You have locked your build server for all players except added team members.
|
||||
BAU_LOCKED_OPEN = §7You have opened your build server for all added players.
|
||||
BAU_DELMEMBER_USAGE = §8/§7build delmember §8[§eplayer§8]
|
||||
BAU_DELMEMBER_USAGE = §8/§7build delmember §8[§eworld/all§8] §8[§eplayer§8]
|
||||
BAU_DELMEMBER_SELFDEL = §cYou cannot remove yourself!
|
||||
BAU_DELMEMBER_DELETED = §cPlayer was removed.
|
||||
BAU_DELMEMBER_DELETED_TARGET = §cYou were removed from the world of §e{0}.
|
||||
@@ -244,9 +244,23 @@ BAU_DELETE_DELETED = §aYour world is being reset.
|
||||
BAU_DELETE_GUI_NAME = §eDo you really want to delete the world?
|
||||
BAU_DELETE_GUI_CANCEL = §cCancel
|
||||
BAU_DELETE_GUI_DELETE = §aDelete
|
||||
BAU_WORLD_GUI_TITLE = §eYour worlds
|
||||
BAU_WORLD_CREATE_USAGE = §8/§7build world create §8[§ename§8] §8[§eversion§8] §8[§etemplate§8]
|
||||
BAU_WORLD_START_USAGE = §8/§7build world start §8[§eworld§8]
|
||||
BAU_WORLD_RENAME_USAGE = §8/§7build world rename §8[§eworld§8] §8[§enew name§8]
|
||||
BAU_WORLD_DELETE_USAGE = §8/§7build world delete §8[§eworld§8]
|
||||
BAU_WORLD_UPGRADE_USAGE = §8/§7build world upgrade §8[§eworld§8] §8[§eversion§8]
|
||||
BAU_WORLD_VERSION = §cUnknown version.
|
||||
BAU_WORLD_UNKNOWN = §cUnknown world.
|
||||
BAU_WORLD_EXISTS = §cA world with this name already exists for this version.
|
||||
BAU_WORLD_LIMIT = §cYou have reached your build world limit of §e{0}§c. Delete one first.
|
||||
BAU_WORLD_OVER_LIMIT_DELETE = §cYou currently have more than your allowed §e{0}§c build worlds. Delete one before creating, renaming, or upgrading worlds.
|
||||
BAU_WORLD_CREATED = §aWorld created.
|
||||
BAU_WORLD_RENAMED = §aWorld renamed.
|
||||
BAU_WORLD_UPGRADED = §aWorld version updated.
|
||||
BAU_START_ALREADY = §cThis server is already starting.
|
||||
BAU_MEMBER_NOMEMBER = §cThis player is no member of your world!
|
||||
BAU_MEMBER_SET_USAGE = §8/§7build {0} §8[§eplayer§8]
|
||||
BAU_MEMBER_SET_USAGE = §8/§7build {0} §8[§eworld/all§8] §8[§eplayer§8]
|
||||
BAU_MEMBER_SET_TARGET = §7You are now a §e{1}§7 on the world of §e{0}§7.
|
||||
BAU_MEMBER_SET = §7The player is now a §e{0}§7.
|
||||
BAU_MEMBER_SET_SPECTATOR = spectator
|
||||
@@ -524,6 +538,20 @@ TEAM_EVENT_LEFT = §7Your team no longer takes part in this event
|
||||
TEAM_EVENT_JOINED = §7Your team now takes part in the event §e{0}§7!
|
||||
TEAM_EVENT_HOW_TO_LEAVE = §7To cancel the participation repeat the command.
|
||||
|
||||
#Team Worlds
|
||||
TEAM_WORLD_GUI_TITLE = §eTeam worlds
|
||||
TEAM_WORLD_CREATE_USAGE = §8/§7team world create §8[§ename§8] §8[§eversion§8] §8[§etemplate§8]
|
||||
TEAM_WORLD_RENAME_USAGE = §8/§7team world rename §8[§eworld§8] §8[§enew name§8]
|
||||
TEAM_WORLD_UPGRADE_USAGE = §8/§7team world upgrade §8[§eworld§8] §8[§eversion§8]
|
||||
TEAM_WORLD_DELETE_USAGE = §8/§7team world delete §8[§eworld§8]
|
||||
TEAM_WORLD_EXISTS = §cA team world with this name already exists for this version.
|
||||
TEAM_WORLD_LIMIT = §cYour team already has the maximum of §e{0}§c worlds. Delete one first.
|
||||
TEAM_WORLD_OVER_LIMIT_DELETE = §cYour team currently has more than the allowed §e{0}§c worlds. Delete one before creating, renaming, or upgrading worlds.
|
||||
TEAM_WORLD_CREATED = §aTeam world created.
|
||||
TEAM_WORLD_RENAMED = §aTeam world renamed.
|
||||
TEAM_WORLD_UPGRADED = §aTeam world version updated.
|
||||
TEAM_WORLD_DELETED = §aTeam world deleted.
|
||||
|
||||
#Team Color
|
||||
TEAM_COLOR_TITLE = Choose color
|
||||
|
||||
@@ -682,7 +710,7 @@ LOCK_LOCALE_CHANGED = §aLanguage saved
|
||||
|
||||
#Builder Cloud
|
||||
BUILDERCLOUD_USAGE = §8/§7buildercloud §8[§eversion§8] §8[§emap§8]
|
||||
BUILDERCLOUD_CREATE_USAGE = §8/§7buildercloud create §8[§eversion§8] §8[§emap§8] §8<§7generator§8>
|
||||
BUILDERCLOUD_CREATE_USAGE = §8/§7buildercloud create §8[§eversion§8] §8[§emap§8] §8[§etemplate§8]
|
||||
BUILDERCLOUD_RENAME_USAGE = §8/§7buildercloud rename §8[§eversion§8] §8[§emap§8] §8[§enew name§8]
|
||||
BUILDERCLOUD_DEPLOY_USAGE = §8/§7deployarena §8[§egamemode§8] §8[§eversion§8] §8[§emap§8]
|
||||
BUILDERCLOUD_DEPLOY_FINISHED = §7Map deployment finished.
|
||||
|
||||
@@ -201,22 +201,22 @@ IGNORE_ALREADY = §cDu ignorierst diesen Spieler bereits.
|
||||
IGNORE_MESSAGE = §7Du ignorierst nun §e{0}§8.
|
||||
|
||||
#BauCommand
|
||||
BAU_ADDMEMBER_USAGE = §8/§7bau addmember §8[§eSpieler§8]
|
||||
BAU_ADDMEMBER_USAGE = §8/§7bau addmember §8[§eWelt/all§8] §8[§eSpieler§8]
|
||||
BAU_ADDMEMBER_SELFADD = §cDu brauchst dich nicht selbst hinzufügen!
|
||||
BAU_ADDMEMBER_ISADDED = §cDieser Spieler ist bereits Mitglied auf deiner Welt.
|
||||
BAU_ADDMEMBER_ADDED = §aDer Spieler wurde zu deiner Welt hinzugefügt.
|
||||
BAU_ADDMEMBER_ADDED_TARGET = §aDu wurdest zu der Welt von §e{0} §ahinzugefügt.
|
||||
BAU_TP_USAGE = §8/§7bau tp §8[§eSpieler§8]
|
||||
BAU_TP_USAGE = §8/§7bau tp §8[§eSpieler§8] §8<§eWelt§8>
|
||||
BAU_TP_NOALLOWED = §cDu darfst dich nicht auf diese Welt teleportieren.
|
||||
BAU_LOCKED_NOALLOWED = §cDer Bauserver ist momentan gesperrt.
|
||||
BAU_LOCK_BLOCKED = §cDeine Bausperre hat den Beitritt von §e{0} §cverhindert.
|
||||
BAU_LOCKED_OPTIONS = §7Bauserver-Sperroptionen§8: §cnobody§8, §eserverteam§8, §eteam_and_serverteam§8, §eteam§8, §aopen
|
||||
BAU_LOCKED_OPTIONS = §8/§7bau lock §8[§eWelt§8] §8[§eOption§8] §7Optionen§8: §cnobody§8, §eserverteam§8, §eteam_and_serverteam§8, §eteam§8, §aopen
|
||||
BAU_LOCKED_NOBODY = §7Du hast deinen Bau für alle Spieler geschlossen.
|
||||
BAU_LOCKED_SERVERTEAM = §7Du hast deinen Bau für alle außer hinzugefügte Serverteammitglieder gesperrt.
|
||||
BAU_LOCKED_TEAM_AND_SERVERTEAM = §7Du hast deinen Bau für alle außer hinzugefügte Teammitglieder und Serverteammitglieder gesperrt.
|
||||
BAU_LOCKED_TEAM = §7Du hast deinen Bau für alle außer hinzugefügte Teammitglieder gesperrt.
|
||||
BAU_LOCKED_OPEN = §7Du hast deinen Bau für alle hinzugefügten Spieler geöffnet.
|
||||
BAU_DELMEMBER_USAGE = §8/§7bau delmember §8[§eSpieler§8]
|
||||
BAU_DELMEMBER_USAGE = §8/§7bau delmember §8[§eWelt/all§8] §8[§eSpieler§8]
|
||||
BAU_DELMEMBER_SELFDEL = §cDu kannst dich nicht selbst entfernen!
|
||||
BAU_DELMEMBER_DELETED = §cDer Spieler wurde entfernt.
|
||||
BAU_DELMEMBER_DELETED_TARGET = §cDu wurdest von der Welt von §e{0} §centfernt.
|
||||
@@ -226,9 +226,23 @@ BAU_DELETE_DELETED = §aDeine Welt wird zurückgesetzt.
|
||||
BAU_DELETE_GUI_NAME = §eWirklich Welt löschen?
|
||||
BAU_DELETE_GUI_CANCEL = §cAbbrechen
|
||||
BAU_DELETE_GUI_DELETE = §aLöschen
|
||||
BAU_WORLD_GUI_TITLE = §eDeine Welten
|
||||
BAU_WORLD_CREATE_USAGE = §8/§7bau world create §8[§eName§8] §8[§eVersion§8] §8[§eTemplate§8]
|
||||
BAU_WORLD_START_USAGE = §8/§7bau world start §8[§eWelt§8]
|
||||
BAU_WORLD_RENAME_USAGE = §8/§7bau world rename §8[§eWelt§8] §8[§eNeuer Name§8]
|
||||
BAU_WORLD_DELETE_USAGE = §8/§7bau world delete §8[§eWelt§8]
|
||||
BAU_WORLD_UPGRADE_USAGE = §8/§7bau world upgrade §8[§eWelt§8] §8[§eVersion§8]
|
||||
BAU_WORLD_VERSION = §cUnbekannte Version.
|
||||
BAU_WORLD_UNKNOWN = §cUnbekannte Welt.
|
||||
BAU_WORLD_EXISTS = §cEine Welt mit diesem Namen existiert bereits in dieser Version.
|
||||
BAU_WORLD_LIMIT = §cDu hast dein Bauwelt-Limit von §e{0}§c erreicht. Lösche zuerst eine Welt.
|
||||
BAU_WORLD_OVER_LIMIT_DELETE = §cDu hast aktuell mehr als die erlaubten §e{0}§c Bauwelten. Lösche eine Welt, bevor du Welten erstellst, umbenennst oder upgradest.
|
||||
BAU_WORLD_CREATED = §aWelt erstellt.
|
||||
BAU_WORLD_RENAMED = §aWelt umbenannt.
|
||||
BAU_WORLD_UPGRADED = §aWelt-Version aktualisiert.
|
||||
BAU_START_ALREADY = §cDer Server startet bereits.
|
||||
BAU_MEMBER_NOMEMBER = §cDer Spieler ist kein Mitglied deiner Welt!
|
||||
BAU_MEMBER_SET_USAGE = §8/§7bau {0} §8[§eSpieler§8]
|
||||
BAU_MEMBER_SET_USAGE = §8/§7bau {0} §8[§eWelt/all§8] §8[§eSpieler§8]
|
||||
BAU_MEMBER_SET_TARGET = §7Du bist nun ein §e{1}§7 auf der Welt von §e{0}§7.
|
||||
BAU_MEMBER_SET = §7Der Spieler ist nun §e{0}§7.
|
||||
BAU_MEMBER_SET_SPECTATOR = Zuschauer
|
||||
@@ -491,6 +505,20 @@ TEAM_EVENT_LEFT = §7Dein Team nimmt nicht mehr am Event teil
|
||||
TEAM_EVENT_JOINED = §7Dein Team nimmt nun am Event §e{0} §7 teil!
|
||||
TEAM_EVENT_HOW_TO_LEAVE = §7Um die Teilnahme abzusagen, wiederhole den Befehl.
|
||||
|
||||
#Team Worlds
|
||||
TEAM_WORLD_GUI_TITLE = §eTeamwelten
|
||||
TEAM_WORLD_CREATE_USAGE = §8/§7team world create §8[§eName§8] §8[§eVersion§8] §8[§eTemplate§8]
|
||||
TEAM_WORLD_RENAME_USAGE = §8/§7team world rename §8[§eWelt§8] §8[§eNeuer Name§8]
|
||||
TEAM_WORLD_UPGRADE_USAGE = §8/§7team world upgrade §8[§eWelt§8] §8[§eVersion§8]
|
||||
TEAM_WORLD_DELETE_USAGE = §8/§7team world delete §8[§eWelt§8]
|
||||
TEAM_WORLD_EXISTS = §cEine Teamwelt mit diesem Namen existiert bereits in dieser Version.
|
||||
TEAM_WORLD_LIMIT = §cDein Team hat bereits das Maximum von §e{0}§c Welten. Lösche zuerst eine Welt.
|
||||
TEAM_WORLD_OVER_LIMIT_DELETE = §cDein Team hat aktuell mehr als die erlaubten §e{0}§c Welten. Lösche eine Welt, bevor du Welten erstellst, umbenennst oder upgradest.
|
||||
TEAM_WORLD_CREATED = §aTeamwelt erstellt.
|
||||
TEAM_WORLD_RENAMED = §aTeamwelt umbenannt.
|
||||
TEAM_WORLD_UPGRADED = §aTeamwelt-Version aktualisiert.
|
||||
TEAM_WORLD_DELETED = §aTeamwelt gelöscht.
|
||||
|
||||
#Team Color
|
||||
TEAM_COLOR_TITLE = Farbe wählen
|
||||
|
||||
@@ -644,7 +672,7 @@ LOCK_LOCALE_CHANGED = §aSprache gespeichert
|
||||
|
||||
#Builder Cloud
|
||||
BUILDERCLOUD_USAGE = §8/§7buildercloud §8[§eVersion§8] §8[§eWelt§8]
|
||||
BUILDERCLOUD_CREATE_USAGE = §8/§7buildercloud create §8[§eVersion§8] §8[§eWelt§8] §8<§7Generator§8>
|
||||
BUILDERCLOUD_CREATE_USAGE = §8/§7buildercloud create §8[§eVersion§8] §8[§eWelt§8] §8[§eTemplate§8]
|
||||
BUILDERCLOUD_RENAME_USAGE = §8/§7buildercloud rename §8[§eVersion§8] §8[§eWElt§8] §8[§eNeuer Name§8]
|
||||
BUILDERCLOUD_VERSION = §cUnbekannte Version.
|
||||
BUILDERCLOUD_EXISTING_MAP = §cWelt existiert bereits.
|
||||
|
||||
@@ -51,11 +51,9 @@ public class ServerStarter {
|
||||
private static final String EVENT_PATH = TMP_DATA + "event/";
|
||||
public static final String TEMP_WORLD_PATH = TMP_DATA + "arenaserver/";
|
||||
|
||||
private static final String WORLDS_FOLDER = "/worlds";
|
||||
public static final String WORLDS_BASE_PATH = WORLDS_FOLDER + "/userworlds";
|
||||
public static final String BUILDER_BASE_PATH = WORLDS_FOLDER + "/builder";
|
||||
|
||||
private static final String WORLDS_STORAGE_BASE_PATH = "/mnt/storage/worlds/userworlds";
|
||||
public static final String WORLDS_BASE_PATH = SteamwarWorld.WORLD_STORAGE + "/";
|
||||
public static final String LEGACY_WORLDS_BASE_PATH = "/worlds/userworlds";
|
||||
public static final String LEGACY_BUILDER_BASE_PATH = "/worlds/builder";
|
||||
|
||||
private File directory = null;
|
||||
private String worldDir = null;
|
||||
@@ -91,7 +89,8 @@ public class ServerStarter {
|
||||
gameMode = mode.configFile.getName().replace(".yml", "");
|
||||
directory = new File(SERVER_PATH, mode.Server.Folder);
|
||||
arguments.put("config", mode.configFile.getName());
|
||||
tempWorld(SERVER_PATH + mode.Server.Folder + "/arenas/" + map);
|
||||
SteamwarWorld arenaWorld = SteamwarWorld.getOrCreateArenaWorld(gameMode, map, version.getVersionSuffix());
|
||||
tempWorld(arenaWorld.setupAndGetStoragePath());
|
||||
startCondition = () -> {
|
||||
if (playersToSend.stream().anyMatch(player -> Subserver.isArena(Subserver.getSubserver(player)))) {
|
||||
playersToSend.forEach(player -> Chatter.of(player).system("FIGHT_IN_ARENA"));
|
||||
@@ -106,7 +105,7 @@ public class ServerStarter {
|
||||
public ServerStarter event(EventFight eventFight) {
|
||||
arena(ArenaMode.getByInternal(eventFight.getSpielmodus()), eventFight.getMap());
|
||||
node = VelocityCore.local;
|
||||
worldDir = EVENT_PATH;
|
||||
worldDir = EVENT_PATH + "/" + serverToWorldName(Event.byId(eventFight.getEventID()).getEventName()) + "/" + eventFight.getStartTime().toLocalDateTime().format(DateTimeFormatter.ISO_LOCAL_DATE);
|
||||
worldCleanup = () -> {
|
||||
};
|
||||
arguments.put("fightID", String.valueOf(eventFight.getFightID()));
|
||||
@@ -153,30 +152,36 @@ public class ServerStarter {
|
||||
}
|
||||
|
||||
public ServerStarter build(ServerVersion version, UUID owner) {
|
||||
this.version = version;
|
||||
SteamwarUser user = SteamwarUser.get(owner);
|
||||
return build(version, SteamwarWorld.getOrCreateBauWorld(user, version.getVersionSuffix()));
|
||||
}
|
||||
|
||||
public ServerStarter build(ServerVersion version, SteamwarWorld world) {
|
||||
this.version = version;
|
||||
directory = version.getServerDirectory("Bau");
|
||||
worldDir = version.getWorldFolder(WORLDS_BASE_PATH);
|
||||
worldName = String.valueOf(SteamwarUser.get(owner).getId());
|
||||
SteamwarUser user = world.getOwner() == null ? null : SteamwarUser.byId(world.getOwner().getValue());
|
||||
worldDir = WORLDS_BASE_PATH;
|
||||
worldName = world.getUuid().toString();
|
||||
checkpoint = true;
|
||||
|
||||
build(owner);
|
||||
if (world.getType() == WorldType.TEAM) {
|
||||
build(world.getUuid());
|
||||
Team team = Team.byId(world.getTeam().getValue());
|
||||
serverNameProvider = port -> team.getTeamKuerzel() + "s Bau";
|
||||
arguments.put("bauTeam", String.valueOf(world.getTeam().getValue()));
|
||||
} else {
|
||||
build(user.getUUID(), world.getUuid());
|
||||
arguments.put("bauOwner", String.valueOf(user.getId()));
|
||||
}
|
||||
arguments.put("bauWorld", world.getUuid().toString());
|
||||
arguments.put("bauType", world.getType().name());
|
||||
|
||||
worldSetup = () -> {
|
||||
File world = new File(worldDir, worldName);
|
||||
if (!world.exists()) {
|
||||
File storage = new File(version.getWorldFolder(WORLDS_STORAGE_BASE_PATH), worldName);
|
||||
|
||||
if (storage.exists()) {
|
||||
node.execute("mv", storage.getPath(), world.getPath());
|
||||
} else {
|
||||
copyWorld(node, new File(directory, "Bauwelt").getPath(), world.getPath());
|
||||
}
|
||||
}
|
||||
};
|
||||
worldSetup = () -> world.setupAndGetStoragePath(new File(directory, "Bauwelt"));
|
||||
|
||||
// Send players to existing server
|
||||
startCondition = () -> {
|
||||
Bauserver subserver = Bauserver.get(owner);
|
||||
Bauserver subserver = Bauserver.getByWorld(world.getUuid());
|
||||
if (subserver != null) {
|
||||
for (Player p : playersToSend) {
|
||||
SubserverSystem.sendPlayer(subserver, p);
|
||||
@@ -184,9 +189,13 @@ public class ServerStarter {
|
||||
return false;
|
||||
}
|
||||
boolean atLeastOneSupervisor = playersToSend.stream().anyMatch(player -> {
|
||||
if (player.getUniqueId().equals(owner)) return true;
|
||||
BauweltMember bauweltMember = BauweltMember.getBauMember(owner, player.getUniqueId());
|
||||
return bauweltMember.isSupervisor();
|
||||
SteamwarUser playerUser = SteamwarUser.get(player.getUniqueId());
|
||||
if (world.getType() == WorldType.TEAM) {
|
||||
return playerUser.getTeam() == world.getTeam().getValue();
|
||||
}
|
||||
if (user != null && playerUser.getId() == user.getId()) return true;
|
||||
BauweltMember bauweltMember = BauweltMember.getBauMember(world.getUuid(), playerUser.getId());
|
||||
return bauweltMember != null && bauweltMember.isSupervisor();
|
||||
});
|
||||
if (!atLeastOneSupervisor) {
|
||||
for (Player p : playersToSend) {
|
||||
@@ -220,15 +229,20 @@ public class ServerStarter {
|
||||
}
|
||||
|
||||
private void build(UUID owner) {
|
||||
constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Bauserver(serverName, owner, port, builder, shutdownCallback, failureCallback);
|
||||
build(owner, owner);
|
||||
}
|
||||
|
||||
private void build(UUID owner, UUID world) {
|
||||
constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Bauserver(serverName, owner, world, port, builder, shutdownCallback, failureCallback);
|
||||
serverNameProvider = port -> bauServerName(SteamwarUser.get(owner));
|
||||
}
|
||||
|
||||
public ServerStarter builder(ServerVersion version, String map, File generator) {
|
||||
public ServerStarter builder(ServerVersion version, String map, File prototype) {
|
||||
this.version = version;
|
||||
directory = version.getServerDirectory("Builder");
|
||||
worldDir = version.getWorldFolder(BUILDER_BASE_PATH);
|
||||
worldName = map;
|
||||
SteamwarWorld world = SteamwarWorld.getOrCreateBuilderWorld(map, version.getVersionSuffix());
|
||||
worldDir = WORLDS_BASE_PATH;
|
||||
worldName = world.getUuid().toString();
|
||||
serverNameProvider = port -> "*" + map;
|
||||
checkpoint = true;
|
||||
constructor = (serverName, port, builder, shutdownCallback, failureCallback) -> new Builderserver(serverName, worldName, port, builder, shutdownCallback, failureCallback);
|
||||
@@ -245,16 +259,7 @@ public class ServerStarter {
|
||||
return true;
|
||||
};
|
||||
|
||||
if (generator != null) {
|
||||
worldSetup = () -> {
|
||||
File leveldat = new File(new File(worldDir, worldName), "level.dat");
|
||||
try {
|
||||
Files.copy(generator.toPath(), leveldat.toPath());
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
worldSetup = () -> world.setupAndGetStoragePath(prototype != null ? prototype : legacyBuilderWorld(version, map));
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -351,13 +356,29 @@ public class ServerStarter {
|
||||
}
|
||||
|
||||
public static String serverToWorldName(String serverName) {
|
||||
return serverName.replace(' ', '_').replace("[", "").replace("]", "");
|
||||
return serverName.replace(' ', '_').replace("[", "").replace("]", "").replace(".", "");
|
||||
}
|
||||
|
||||
public static void copyWorld(Node node, String template, String target) {
|
||||
node.execute("cp", "-r", template, target);
|
||||
}
|
||||
|
||||
private static File legacyBauWorld(ServerVersion version, SteamwarUser user) {
|
||||
File legacyIdWorld = new File(version.getWorldFolder(LEGACY_WORLDS_BASE_PATH), String.valueOf(user.getId()));
|
||||
if(legacyIdWorld.exists())
|
||||
return legacyIdWorld;
|
||||
|
||||
File legacyUuidWorld = new File(version.getWorldFolder(LEGACY_WORLDS_BASE_PATH), user.getUUID().toString());
|
||||
if(legacyUuidWorld.exists())
|
||||
return legacyUuidWorld;
|
||||
|
||||
return new File(version.getServerDirectory("Bau"), "Bauwelt");
|
||||
}
|
||||
|
||||
public static File legacyBuilderWorld(ServerVersion version, String map) {
|
||||
return new File(version.getWorldFolder(LEGACY_BUILDER_BASE_PATH), map);
|
||||
}
|
||||
|
||||
private interface ServerConstructor {
|
||||
Subserver construct(String serverName, int port, ProcessBuilder builder, Runnable shutdownCallback, Consumer<Exception> failureCallback);
|
||||
}
|
||||
@@ -396,4 +417,4 @@ public class ServerStarter {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,16 +35,19 @@ import de.steamwar.persistent.Bauserver;
|
||||
import de.steamwar.sql.BauweltMember;
|
||||
import de.steamwar.sql.GameModeConfig;
|
||||
import de.steamwar.sql.SteamwarUser;
|
||||
import de.steamwar.velocitycore.ServerStarter;
|
||||
import de.steamwar.velocitycore.ServerVersion;
|
||||
import de.steamwar.velocitycore.SubserverSystem;
|
||||
import de.steamwar.velocitycore.VelocityCore;
|
||||
import de.steamwar.sql.SteamwarWorld;
|
||||
import de.steamwar.sql.UserPerm;
|
||||
import de.steamwar.velocitycore.*;
|
||||
import de.steamwar.velocitycore.inventory.SWInventory;
|
||||
import de.steamwar.velocitycore.inventory.SWItem;
|
||||
import de.steamwar.velocitycore.inventory.SWListInv;
|
||||
import de.steamwar.velocitycore.network.NetworkSender;
|
||||
import de.steamwar.velocitycore.util.BauLock;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Linked
|
||||
@@ -64,13 +67,13 @@ public class BauCommand extends SWCommand {
|
||||
}
|
||||
|
||||
@Register
|
||||
public void toBau(PlayerChatter sender, @OptionalValue(value = "", onlyUINIG = true) ServerVersion version) {
|
||||
new ServerStarter().build(version, sender.user().getUUID()).send(sender.getPlayer()).start();
|
||||
public void toBau(PlayerChatter sender, @Mapper("ownBauWorld") @OptionalValue(value = "", onlyUINIG = true) @AllowNull SteamwarWorld world) {
|
||||
startPersonalWorld(sender, world);
|
||||
}
|
||||
|
||||
@Register(value = "addmember", description = "BAU_ADDMEMBER_USAGE")
|
||||
public void addmember(Chatter sender, @Validator("addMemberTarget") SteamwarUser target) {
|
||||
BauweltMember.addMember(sender.user().getUUID(), target.getUUID());
|
||||
public void addmember(Chatter sender, @Mapper("bauWorldSelector") String world, @Validator("addMemberTarget") SteamwarUser target) {
|
||||
if (!forEachSelectedWorld(sender, world, bauworld -> BauweltMember.addMember(bauworld.getUuid(), target.getId()))) return;
|
||||
sender.system("BAU_ADDMEMBER_ADDED");
|
||||
Chatter.of(target.getUUID()).system("BAU_ADDMEMBER_ADDED_TARGET", sender);
|
||||
}
|
||||
@@ -86,18 +89,31 @@ public class BauCommand extends SWCommand {
|
||||
messageSender.send("BAU_ADDMEMBER_SELFADD");
|
||||
return false;
|
||||
}
|
||||
if (BauweltMember.getBauMember(sender.user().getId(), value.getId()) != null) {
|
||||
messageSender.send("BAU_ADDMEMBER_ISADDED");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
@Register(value = "tp", description = "BAU_TP_USAGE")
|
||||
@Register("teleport")
|
||||
public void teleport(PlayerChatter sender, @Validator("teleportTarget") SteamwarUser worldOwner, @OptionalValue(value = "", onlyUINIG = true) ServerVersion version) {
|
||||
new ServerStarter().build(version, worldOwner.getUUID()).send(sender.getPlayer()).start();
|
||||
public void teleport(PlayerChatter sender, @Validator("teleportTarget") SteamwarUser worldOwner, @Mapper("targetBauWorld") @OptionalValue(value = "", onlyUINIG = true) @AllowNull SteamwarWorld world) {
|
||||
if (world == null) {
|
||||
world = defaultPersonalWorld(worldOwner);
|
||||
}
|
||||
if (world == null) {
|
||||
sender.system("BAU_WORLD_UNKNOWN");
|
||||
return;
|
||||
}
|
||||
if (sender.user().getId() != worldOwner.getId() && BauLock.isLocked(world, sender.user())) {
|
||||
sender.system("BAU_LOCKED_NOALLOWED");
|
||||
Chatter.of(worldOwner.getUUID()).system("BAU_LOCK_BLOCKED", sender);
|
||||
return;
|
||||
}
|
||||
if (sender.user().getId() != worldOwner.getId() && BauweltMember.getBauMember(world.getUuid(), sender.user().getId()) == null) {
|
||||
SubserverSystem.sendDeniedMessage(sender, worldOwner.getUUID());
|
||||
sender.system("BAU_TP_NOALLOWED");
|
||||
return;
|
||||
}
|
||||
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
|
||||
}
|
||||
|
||||
@Validator(value = "teleportTarget", local = true)
|
||||
@@ -107,17 +123,6 @@ public class BauCommand extends SWCommand {
|
||||
messageSender.send("UNKNOWN_PLAYER");
|
||||
return false;
|
||||
}
|
||||
if (sender.user().getId() != owner.getId() && BauweltMember.getBauMember(owner.getId(), sender.user().getId()) == null) {
|
||||
SubserverSystem.sendDeniedMessage(sender, owner.getUUID());
|
||||
messageSender.send("BAU_TP_NOALLOWED");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BauLock.isLocked(owner, sender.user())) {
|
||||
messageSender.send("BAU_LOCKED_NOALLOWED");
|
||||
Chatter.of(owner.getUUID()).system("BAU_LOCK_BLOCKED", sender);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
@@ -128,39 +133,39 @@ public class BauCommand extends SWCommand {
|
||||
}
|
||||
|
||||
@Register("setspectator")
|
||||
public void setSpectator(Chatter sender, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
|
||||
setPerms(sender, user, "setspectator", "BAU_MEMBER_SET_SPECTATOR", member -> {
|
||||
public void setSpectator(Chatter sender, @Mapper("bauWorldSelector") String world, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
|
||||
setPerms(sender, world, user, "setspectator", "BAU_MEMBER_SET_SPECTATOR", member -> {
|
||||
member.setBuild(false);
|
||||
member.setSupervisor(false);
|
||||
});
|
||||
}
|
||||
|
||||
@Register("setbuilder")
|
||||
public void setBuilder(Chatter sender, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
|
||||
setPerms(sender, user, "setbuild", "BAU_MEMBER_SET_BUILDER", member -> {
|
||||
public void setBuilder(Chatter sender, @Mapper("bauWorldSelector") String world, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
|
||||
setPerms(sender, world, user, "setbuild", "BAU_MEMBER_SET_BUILDER", member -> {
|
||||
member.setBuild(true);
|
||||
member.setSupervisor(false);
|
||||
});
|
||||
}
|
||||
|
||||
@Register("setsupervisor")
|
||||
public void setSupervisor(Chatter sender, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
|
||||
setPerms(sender, user, "setsupervisor", "BAU_MEMBER_SET_SUPERVISOR", member -> {
|
||||
public void setSupervisor(Chatter sender, @Mapper("bauWorldSelector") String world, @Mapper("addedUsers") @AllowNull @OptionalValue("") SteamwarUser user) {
|
||||
setPerms(sender, world, user, "setsupervisor", "BAU_MEMBER_SET_SUPERVISOR", member -> {
|
||||
member.setBuild(true);
|
||||
member.setSupervisor(true);
|
||||
});
|
||||
}
|
||||
|
||||
private void setPerms(Chatter owner, SteamwarUser user, String name, String permName, Consumer<BauweltMember> setter) {
|
||||
private void setPerms(Chatter owner, String world, SteamwarUser user, String name, String permName, Consumer<BauweltMember> setter) {
|
||||
if (user == null) {
|
||||
owner.system("BAU_MEMBER_SET_USAGE", name);
|
||||
return;
|
||||
}
|
||||
|
||||
withMember(owner, user, target -> {
|
||||
withMembers(owner, world, user, target -> {
|
||||
setter.accept(target);
|
||||
|
||||
Bauserver bauserver = Bauserver.get(owner.user().getUUID());
|
||||
Bauserver bauserver = Bauserver.getByWorld(target.getWorldID());
|
||||
if (bauserver != null) {
|
||||
bauserver.getRegisteredServer().getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaumemberUpdatePacket()));
|
||||
}
|
||||
@@ -171,11 +176,11 @@ public class BauCommand extends SWCommand {
|
||||
}
|
||||
|
||||
@Register(value = "delmember", description = "BAU_DELMEMBER_USAGE")
|
||||
public void delmember(Chatter owner, @Mapper("addedUsers") SteamwarUser user) {
|
||||
withMember(owner, user, target -> {
|
||||
public void delmember(Chatter owner, @Mapper("bauWorldSelector") String world, @Mapper("addedUsers") SteamwarUser user) {
|
||||
withMembers(owner, world, user, target -> {
|
||||
target.remove();
|
||||
|
||||
Bauserver bauserver = Bauserver.get(owner.user().getUUID());
|
||||
Bauserver bauserver = Bauserver.getByWorld(target.getWorldID());
|
||||
Chatter member = Chatter.of(user.getUUID());
|
||||
member.withPlayer(player -> {
|
||||
if (bauserver != null && bauserver.getRegisteredServer().getPlayersConnected().contains(player)) {
|
||||
@@ -192,6 +197,29 @@ public class BauCommand extends SWCommand {
|
||||
});
|
||||
}
|
||||
|
||||
@Mapper(value = "bauWorldSelector", local = true)
|
||||
public TypeMapper<String> bauWorldSelector() {
|
||||
return new TypeMapper<>() {
|
||||
@Override
|
||||
public String map(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
if ("all".equalsIgnoreCase(s)) return "all";
|
||||
return SteamwarWorld.getBauWorlds(sender.user()).stream()
|
||||
.map(SteamwarWorld::getName)
|
||||
.filter(name -> name.equalsIgnoreCase(s))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
List<String> worlds = new ArrayList<>();
|
||||
worlds.add("all");
|
||||
SteamwarWorld.getBauWorlds(sender.user()).stream().map(SteamwarWorld::getName).forEach(worlds::add);
|
||||
return worlds;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Mapper(value = "addedUsers", local = true)
|
||||
public TypeMapper<SteamwarUser> addedUsers() {
|
||||
return new TypeMapper<SteamwarUser>() {
|
||||
@@ -202,8 +230,10 @@ public class BauCommand extends SWCommand {
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
return BauweltMember.getMembers(sender.user().getId()).stream()
|
||||
return selectedWorlds(sender, previousArguments.userArgs.length == 0 ? "all" : previousArguments.userArgs[previousArguments.userArgs.length - 1]).stream()
|
||||
.flatMap(world -> BauweltMember.getWorldMembers(world.getUuid()).stream())
|
||||
.map(bauweltMember -> SteamwarUser.byId(bauweltMember.getMemberID()).getUserName())
|
||||
.distinct()
|
||||
.toList();
|
||||
}
|
||||
};
|
||||
@@ -213,9 +243,11 @@ public class BauCommand extends SWCommand {
|
||||
public void stop(PlayerChatter sender) {
|
||||
VelocityCore.schedule(() -> {
|
||||
sender.system("BAU_STOPPING");
|
||||
Bauserver subserver = Bauserver.get(sender.user().getUUID());
|
||||
if (subserver != null) {
|
||||
subserver.stop();
|
||||
for (SteamwarWorld world : SteamwarWorld.getBauWorlds(sender.user())) {
|
||||
Bauserver subserver = Bauserver.getByWorld(world.getUuid());
|
||||
if (subserver != null) {
|
||||
subserver.stop();
|
||||
}
|
||||
}
|
||||
|
||||
sender.system("BAU_STOPPED");
|
||||
@@ -224,25 +256,50 @@ public class BauCommand extends SWCommand {
|
||||
|
||||
@Register("resetall")
|
||||
@Register("delete")
|
||||
public void delete(PlayerChatter sender, ServerVersion version) {
|
||||
SWInventory inventory = new SWInventory(sender, 9, new Message("BAU_DELETE_GUI_NAME"));
|
||||
inventory.addItem(0, new SWItem("LIME_DYE", new Message("BAU_DELETE_GUI_DELETE")), click -> {
|
||||
String world = version.getWorldFolder(ServerStarter.WORLDS_BASE_PATH) + sender.user().getId();
|
||||
public void delete(PlayerChatter sender, @Mapper("ownBauWorld") SteamwarWorld world) {
|
||||
openDeleteWorld(sender, world);
|
||||
}
|
||||
|
||||
VelocityCore.schedule(() -> {
|
||||
Bauserver subserver = Bauserver.get(sender.user().getUUID());
|
||||
if (subserver != null) {
|
||||
subserver.stop();
|
||||
}
|
||||
@Register("worlds")
|
||||
@Register("world")
|
||||
public void worlds(PlayerChatter sender) {
|
||||
openWorldList(sender);
|
||||
}
|
||||
|
||||
SubserverSystem.deleteFolder(VelocityCore.local, world);
|
||||
sender.system("BAU_DELETE_DELETED");
|
||||
}).schedule();
|
||||
@Register(value = "world create", description = "BAU_WORLD_CREATE_USAGE")
|
||||
public void createWorld(PlayerChatter sender, String name, @ErrorMessage("BAU_WORLD_VERSION") ServerVersion version, @Mapper("templateWorld") SteamwarWorld template) {
|
||||
SteamwarWorld world = createPersonalWorld(sender, name, version, template);
|
||||
if (world != null) {
|
||||
sender.system("BAU_WORLD_CREATED");
|
||||
}
|
||||
}
|
||||
|
||||
inventory.close();
|
||||
});
|
||||
inventory.addItem(8, new SWItem("RED_DYE", new Message("BAU_DELETE_GUI_CANCEL")), click -> inventory.close());
|
||||
inventory.open();
|
||||
@Register(value = "world start", description = "BAU_WORLD_START_USAGE")
|
||||
public void startWorld(PlayerChatter sender, @Mapper("ownBauWorld") SteamwarWorld world) {
|
||||
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
|
||||
}
|
||||
|
||||
@Register(value = "world rename", description = "BAU_WORLD_RENAME_USAGE")
|
||||
public void renameWorld(Chatter sender, @Mapper("ownBauWorld") SteamwarWorld world, String newName) {
|
||||
if (hasTooManyPersonalWorlds(sender)) return;
|
||||
if (SteamwarWorld.getBauWorld(sender.user(), newName, world.getVersion()) != null) {
|
||||
sender.system("BAU_WORLD_EXISTS");
|
||||
return;
|
||||
}
|
||||
world.rename(newName);
|
||||
sender.system("BAU_WORLD_RENAMED");
|
||||
}
|
||||
|
||||
@Register(value = "world delete", description = "BAU_WORLD_DELETE_USAGE")
|
||||
public void deleteWorld(PlayerChatter sender, @Mapper("ownBauWorld") SteamwarWorld world) {
|
||||
openDeleteWorld(sender, world);
|
||||
}
|
||||
|
||||
@Register(value = "world upgrade", description = "BAU_WORLD_UPGRADE_USAGE")
|
||||
public void upgradeWorld(Chatter sender, @Mapper("ownBauWorld") SteamwarWorld world, @ErrorMessage("BAU_WORLD_VERSION") ServerVersion version) {
|
||||
if (hasTooManyPersonalWorlds(sender)) return;
|
||||
world.changeVersion(version.getVersionSuffix());
|
||||
sender.system("BAU_WORLD_UPGRADED");
|
||||
}
|
||||
|
||||
@Register("test")
|
||||
@@ -254,27 +311,244 @@ public class BauCommand extends SWCommand {
|
||||
}
|
||||
|
||||
@Register(value = "lock", description = "BAU_LOCKED_OPTIONS")
|
||||
public void lock(Chatter sender, BauLockState bauLockState) {
|
||||
BauLock.setLocked(sender, bauLockState);
|
||||
public void lock(Chatter sender, @Mapper("ownBauWorld") SteamwarWorld world, BauLockState bauLockState) {
|
||||
BauLock.setLocked(sender, world, bauLockState);
|
||||
}
|
||||
|
||||
@Register("unlock")
|
||||
public void unlock(Chatter sender) {
|
||||
BauLock.setLocked(sender, BauLockState.OPEN);
|
||||
public void unlock(Chatter sender, @Mapper("ownBauWorld") SteamwarWorld world) {
|
||||
BauLock.setLocked(sender, world, BauLockState.OPEN);
|
||||
}
|
||||
|
||||
private static void withMember(Chatter owner, SteamwarUser member, Consumer<BauweltMember> function) {
|
||||
private static void withMembers(Chatter owner, String worldSelector, SteamwarUser member, Consumer<BauweltMember> function) {
|
||||
if (member == null) {
|
||||
owner.system("UNKNOWN_PLAYER");
|
||||
return;
|
||||
}
|
||||
|
||||
BauweltMember target = BauweltMember.getBauMember(owner.user().getId(), member.getId());
|
||||
if (target == null) {
|
||||
owner.system("BAU_MEMBER_NOMEMBER");
|
||||
List<SteamwarWorld> worlds = selectedWorlds(owner, worldSelector);
|
||||
if (worlds.isEmpty()) {
|
||||
owner.system("BAU_WORLD_UNKNOWN");
|
||||
return;
|
||||
}
|
||||
|
||||
function.accept(target);
|
||||
boolean found = false;
|
||||
for (SteamwarWorld world : worlds) {
|
||||
BauweltMember target = BauweltMember.getBauMember(world.getUuid(), member.getId());
|
||||
if (target == null) continue;
|
||||
found = true;
|
||||
function.accept(target);
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
owner.system("BAU_MEMBER_NOMEMBER");
|
||||
}
|
||||
}
|
||||
|
||||
@Mapper(value = "ownBauWorld", local = true)
|
||||
private TypeMapper<SteamwarWorld> ownBauWorldMapper() {
|
||||
return new TypeMapper<SteamwarWorld>() {
|
||||
@Override
|
||||
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
if (s == null || s.isEmpty()) return defaultPersonalWorld(sender);
|
||||
return SteamwarWorld.getBauWorlds(sender.user()).stream()
|
||||
.filter(world -> world.getName().equalsIgnoreCase(s) || world.getUuid().toString().equalsIgnoreCase(s))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
return SteamwarWorld.getBauWorlds(sender.user()).stream().map(SteamwarWorld::getName).toList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Mapper(value = "targetBauWorld", local = true)
|
||||
private TypeMapper<SteamwarWorld> targetBauWorldMapper() {
|
||||
return new TypeMapper<>() {
|
||||
@Override
|
||||
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
SteamwarUser owner = previousArguments.getAll(SteamwarUser.class).stream().findFirst().orElse(null);
|
||||
if (owner == null) return null;
|
||||
if (s == null || s.isEmpty()) return defaultPersonalWorld(owner);
|
||||
return SteamwarWorld.getBauWorlds(owner).stream()
|
||||
.filter(world -> world.getName().equalsIgnoreCase(s) || world.getUuid().toString().equalsIgnoreCase(s))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
SteamwarUser owner = previousArguments.getAll(SteamwarUser.class).stream().findFirst().orElse(null);
|
||||
if (owner == null) return List.of();
|
||||
return SteamwarWorld.getBauWorlds(owner).stream().map(SteamwarWorld::getName).toList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Mapper(value = "templateWorld", local = true)
|
||||
private TypeMapper<SteamwarWorld> templateWorldMapper() {
|
||||
return new TypeMapper<>() {
|
||||
@Override
|
||||
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
|
||||
if (version == null) return null;
|
||||
return SteamwarWorld.getTemplateWorld(s, version.getVersionSuffix());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
|
||||
if (version == null) return List.of();
|
||||
return SteamwarWorld.getTemplateWorlds(version.getVersionSuffix()).stream().map(SteamwarWorld::getName).toList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void openWorldList(PlayerChatter sender) {
|
||||
List<SWListInv.SWListEntry<SteamwarWorld>> entries = new ArrayList<>();
|
||||
for (SteamwarWorld world : SteamwarWorld.getBauWorlds(sender.user())) {
|
||||
SWItem item = new SWItem("GRASS_BLOCK", new Message("PLAIN_STRING", "§e" + world.getName()))
|
||||
.addLore(new Message("PLAIN_STRING", "§7Version: §e" + world.getVersion()))
|
||||
.addLore(new Message("PLAIN_STRING", "§7Left: start, right: manage"));
|
||||
entries.add(new SWListInv.SWListEntry<>(item, world));
|
||||
}
|
||||
SWListInv<SteamwarWorld> inventory = new SWListInv<>(sender, new Message("BAU_WORLD_GUI_TITLE"), entries, (click, world) -> {
|
||||
if (click.isRightClick()) {
|
||||
openWorldManagement(sender, world);
|
||||
} else {
|
||||
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
|
||||
}
|
||||
});
|
||||
inventory.open();
|
||||
}
|
||||
|
||||
private void openWorldManagement(PlayerChatter sender, SteamwarWorld world) {
|
||||
SWInventory inventory = new SWInventory(sender, 9, new Message("PLAIN_STRING", "§e" + world.getName()));
|
||||
inventory.addItem(0, new SWItem("ENDER_PEARL", new Message("PLAIN_STRING", "§aStart")), click -> {
|
||||
inventory.close();
|
||||
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
|
||||
});
|
||||
inventory.addItem(3, new SWItem("EXPERIENCE_BOTTLE", new Message("PLAIN_STRING", "§eUpgrade to latest")), click -> {
|
||||
if (hasTooManyPersonalWorlds(sender)) {
|
||||
inventory.close();
|
||||
return;
|
||||
}
|
||||
world.changeVersion(ServerVersion.PAPER_21.getVersionSuffix());
|
||||
sender.system("BAU_WORLD_UPGRADED");
|
||||
inventory.close();
|
||||
});
|
||||
inventory.addItem(8, new SWItem("BARRIER", new Message("PLAIN_STRING", "§cDelete")), click -> openDeleteWorld(sender, world));
|
||||
inventory.open();
|
||||
}
|
||||
|
||||
private void openDeleteWorld(PlayerChatter sender, SteamwarWorld world) {
|
||||
SWInventory inventory = new SWInventory(sender, 9, new Message("BAU_DELETE_GUI_NAME"));
|
||||
inventory.addItem(0, new SWItem("LIME_DYE", new Message("BAU_DELETE_GUI_DELETE")), click -> {
|
||||
VelocityCore.schedule(() -> {
|
||||
Bauserver subserver = Bauserver.getByWorld(world.getUuid());
|
||||
if (subserver != null) {
|
||||
subserver.stop();
|
||||
}
|
||||
world.markDeleted();
|
||||
SubserverSystem.deleteFolder(VelocityCore.local, world.getStorageDirectory().getPath());
|
||||
sender.system("BAU_DELETE_DELETED");
|
||||
}).schedule();
|
||||
inventory.close();
|
||||
});
|
||||
inventory.addItem(8, new SWItem("RED_DYE", new Message("BAU_DELETE_GUI_CANCEL")), click -> inventory.close());
|
||||
inventory.open();
|
||||
}
|
||||
|
||||
private void startPersonalWorld(PlayerChatter sender, SteamwarWorld world) {
|
||||
if (world == null) {
|
||||
world = defaultPersonalWorld(sender);
|
||||
}
|
||||
if (world == null) {
|
||||
sender.system("BAU_WORLD_UNKNOWN");
|
||||
return;
|
||||
}
|
||||
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
|
||||
}
|
||||
|
||||
private static SteamwarWorld defaultPersonalWorld(Chatter sender) {
|
||||
return defaultPersonalWorld(sender.user());
|
||||
}
|
||||
|
||||
private static SteamwarWorld defaultPersonalWorld(SteamwarUser user) {
|
||||
return SteamwarWorld.getBauWorlds(user).stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
private static List<SteamwarWorld> selectedWorlds(Chatter sender, String selector) {
|
||||
List<SteamwarWorld> worlds = SteamwarWorld.getBauWorlds(sender.user());
|
||||
if ("all".equalsIgnoreCase(selector)) {
|
||||
return worlds;
|
||||
}
|
||||
String normalized = selector.toLowerCase(Locale.ROOT);
|
||||
return worlds.stream()
|
||||
.filter(world -> world.getName().toLowerCase(Locale.ROOT).equals(normalized))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static boolean forEachSelectedWorld(Chatter sender, String selector, Consumer<SteamwarWorld> consumer) {
|
||||
List<SteamwarWorld> worlds = selectedWorlds(sender, selector);
|
||||
if (worlds.isEmpty()) {
|
||||
sender.system("BAU_WORLD_UNKNOWN");
|
||||
return false;
|
||||
}
|
||||
worlds.forEach(consumer);
|
||||
return true;
|
||||
}
|
||||
|
||||
private SteamwarWorld createPersonalWorld(Chatter sender, String name, ServerVersion version, SteamwarWorld template) {
|
||||
if (template == null || template.getVersion() != version.getVersionSuffix()) {
|
||||
sender.system("BAU_WORLD_UNKNOWN");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (sender.user().hasPerm(UserPerm.BUILD_UNLIMITED_WORLDS)) {
|
||||
if (SteamwarWorld.getBauWorld(sender.user(), name, version.getVersionSuffix()) != null) {
|
||||
sender.system("BAU_WORLD_EXISTS");
|
||||
return null;
|
||||
}
|
||||
return SteamwarWorld.getOrCreateBauWorld(sender.user(), name, version.getVersionSuffix(), template.getStorageDirectory());
|
||||
}
|
||||
|
||||
long limit = personalWorldLimit(sender);
|
||||
long count = SteamwarWorld.countBauWorlds(sender.user());
|
||||
if (count > limit) {
|
||||
sender.system("BAU_WORLD_OVER_LIMIT_DELETE", limit);
|
||||
return null;
|
||||
}
|
||||
if (count >= limit) {
|
||||
sender.system("BAU_WORLD_LIMIT", limit);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (SteamwarWorld.getBauWorld(sender.user(), name, version.getVersionSuffix()) != null) {
|
||||
sender.system("BAU_WORLD_EXISTS");
|
||||
return null;
|
||||
}
|
||||
|
||||
return SteamwarWorld.getOrCreateBauWorld(sender.user(), name, version.getVersionSuffix(), template.getStorageDirectory());
|
||||
}
|
||||
|
||||
private boolean hasTooManyPersonalWorlds(Chatter sender) {
|
||||
if (sender.user().hasPerm(UserPerm.BUILD_UNLIMITED_WORLDS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long limit = personalWorldLimit(sender);
|
||||
if (SteamwarWorld.countBauWorlds(sender.user()) <= limit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sender.system("BAU_WORLD_OVER_LIMIT_DELETE", limit);
|
||||
return true;
|
||||
}
|
||||
|
||||
private long personalWorldLimit(Chatter sender) {
|
||||
return sender.user().hasPerm(UserPerm.BUILD_EXTRA_WORLDS) ? SteamwarWorld.EXTRA_BAU_WORLD_LIMIT : SteamwarWorld.DEFAULT_BAU_WORLD_LIMIT;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import de.steamwar.linkage.Linked;
|
||||
import de.steamwar.messages.Chatter;
|
||||
import de.steamwar.messages.PlayerChatter;
|
||||
import de.steamwar.sql.GameModeConfig;
|
||||
import de.steamwar.sql.SteamwarWorld;
|
||||
import de.steamwar.sql.UserPerm;
|
||||
import de.steamwar.velocitycore.ArenaMode;
|
||||
import de.steamwar.velocitycore.ServerStarter;
|
||||
@@ -33,11 +34,11 @@ import de.steamwar.velocitycore.ServerVersion;
|
||||
import de.steamwar.velocitycore.VelocityCore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Linked
|
||||
public class BuilderCloudCommand extends SWCommand {
|
||||
@@ -47,14 +48,23 @@ public class BuilderCloudCommand extends SWCommand {
|
||||
}
|
||||
|
||||
@Register(value = "create", description = "BUILDERCLOUD_CREATE_USAGE")
|
||||
public void create(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map, @OptionalValue("") @Mapper("generator") @AllowNull File generator) {
|
||||
mapFile(version, map).mkdir();
|
||||
sender.withPlayer(p -> new ServerStarter().builder(version, map, generator).send(p).start());
|
||||
public void create(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map, @Mapper("templateWorld") SteamwarWorld template) {
|
||||
if(mapExists(version, map)) {
|
||||
sender.system("BUILDERCLOUD_EXISTING_MAP");
|
||||
return;
|
||||
}
|
||||
if (template == null || template.getVersion() != version.getVersionSuffix()) {
|
||||
sender.system("BAU_WORLD_UNKNOWN");
|
||||
return;
|
||||
}
|
||||
|
||||
SteamwarWorld.getOrCreateBuilderWorld(map, version.getVersionSuffix(), template.getStorageDirectory());
|
||||
sender.withPlayer(p -> new ServerStarter().builder(version, map, template.getStorageDirectory()).send(p).start());
|
||||
}
|
||||
|
||||
@Register(description = "BUILDERCLOUD_USAGE")
|
||||
public void start(PlayerChatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map) {
|
||||
if (!mapFile(version, map).exists()) {
|
||||
if (!mapExists(version, map)) {
|
||||
sender.system("BUILDERCLOUD_UNKNOWN_MAP");
|
||||
return;
|
||||
}
|
||||
@@ -64,36 +74,34 @@ public class BuilderCloudCommand extends SWCommand {
|
||||
|
||||
@Register(value = "rename", description = "BUILDERCLOUD_RENAME_USAGE")
|
||||
public void rename(Chatter sender, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String oldName, String newName) {
|
||||
File oldMap = mapFile(version, oldName);
|
||||
if (!oldMap.exists()) {
|
||||
SteamwarWorld oldMap = builderWorld(version, oldName);
|
||||
if (oldMap == null) {
|
||||
sender.system("BUILDERCLOUD_UNKNOWN_MAP");
|
||||
return;
|
||||
}
|
||||
|
||||
File newMap = mapFile(version, newName);
|
||||
if (newMap.exists()) {
|
||||
if (mapExists(version, newName)) {
|
||||
sender.system("BUILDERCLOUD_EXISTING_MAP");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Files.move(oldMap.toPath(), newMap.toPath());
|
||||
} catch (IOException e) {
|
||||
throw new SecurityException(e);
|
||||
}
|
||||
oldMap.rename(newName);
|
||||
|
||||
sender.system("BUILDERCLOUD_RENAMED");
|
||||
}
|
||||
|
||||
@Register(value = "deploy", description = "BUILDERCLOUD_DEPLOY_USAGE")
|
||||
public void deploy(Chatter sender, @Mapper("nonHistoricArenaMode") GameModeConfig<String, String> arenaMode, @ErrorMessage("BUILDERCLOUD_VERSION") ServerVersion version, @Mapper("map") String map) {
|
||||
if (!mapFile(version, map).exists()) {
|
||||
SteamwarWorld builderWorld = builderWorld(version, map);
|
||||
if (builderWorld == null) {
|
||||
sender.system("BUILDERCLOUD_UNKNOWN_MAP");
|
||||
return;
|
||||
}
|
||||
|
||||
VelocityCore.schedule(() -> {
|
||||
VelocityCore.local.execute("deployarena.py", arenaMode.configFile.getName(), Integer.toString(version.getVersionSuffix()), map);
|
||||
String modeName = arenaMode.configFile.getName().replace(".yml", "");
|
||||
SteamwarWorld arenaWorld = SteamwarWorld.getOrCreateArenaWorld(modeName, map, version.getVersionSuffix());
|
||||
VelocityCore.local.execute("deployarena.py", arenaMode.configFile.getName(), Integer.toString(version.getVersionSuffix()), map, builderWorld.setupAndGetStoragePath(), arenaWorld.setupAndGetStoragePath());
|
||||
ArenaMode.init();
|
||||
sender.system("BUILDERCLOUD_DEPLOY_FINISHED");
|
||||
}).schedule();
|
||||
@@ -111,60 +119,78 @@ public class BuilderCloudCommand extends SWCommand {
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
File folder = getWorldFolder(previousArguments, 1);
|
||||
|
||||
String[] files;
|
||||
if (folder == null || (files = folder.list()) == null) {
|
||||
ServerVersion version = getVersion(previousArguments, 1);
|
||||
if (version == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Arrays.stream(files).filter(file -> new File(folder, file).isDirectory()).filter(file -> s.startsWith(".") || !file.startsWith(".")).toList();
|
||||
Set<String> maps = new LinkedHashSet<>();
|
||||
SteamwarWorld.getBuilderWorlds(version.getVersionSuffix()).stream()
|
||||
.map(SteamwarWorld::getName)
|
||||
.forEach(maps::add);
|
||||
|
||||
File legacyFolder = new File(version.getWorldFolder(ServerStarter.LEGACY_BUILDER_BASE_PATH));
|
||||
String[] files = legacyFolder.list();
|
||||
if(files != null)
|
||||
Arrays.stream(files).filter(file -> new File(legacyFolder, file).isDirectory()).forEach(maps::add);
|
||||
|
||||
return maps.stream()
|
||||
.filter(file -> s.startsWith(".") || !file.startsWith("."))
|
||||
.toList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Cached(global = true)
|
||||
@Mapper(value = "generator", local = true)
|
||||
private TypeMapper<File> generatorTypeMapper() {
|
||||
@Mapper(value = "templateWorld", local = true)
|
||||
private TypeMapper<SteamwarWorld> templateWorldMapper() {
|
||||
|
||||
return new TypeMapper<File>() {
|
||||
return new TypeMapper<>() {
|
||||
@Override
|
||||
public File map(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
if (s.isEmpty()) return null;
|
||||
|
||||
File folder = getWorldFolder(previousArguments, 2);
|
||||
|
||||
if (folder == null) throw new SecurityException();
|
||||
|
||||
File generator = new File(folder, s + ".dat");
|
||||
if (!generator.exists() || !generator.isFile()) {
|
||||
throw new SecurityException();
|
||||
}
|
||||
|
||||
return generator;
|
||||
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
|
||||
if (version == null) return null;
|
||||
return SteamwarWorld.getTemplateWorld(s, version.getVersionSuffix());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
File folder = getWorldFolder(previousArguments, 2);
|
||||
|
||||
String[] files;
|
||||
if (folder == null || (files = folder.list()) == null) {
|
||||
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
|
||||
if (version == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Arrays.stream(files).filter(file -> new File(folder, file).isFile()).filter(file -> file.endsWith(".dat")).map(file -> file.substring(0, file.length() - 4)).toList();
|
||||
return SteamwarWorld.getTemplateWorlds(version.getVersionSuffix()).stream()
|
||||
.map(SteamwarWorld::getName)
|
||||
.toList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private File mapFile(ServerVersion version, String map) {
|
||||
return new File(version.getWorldFolder(ServerStarter.BUILDER_BASE_PATH), map);
|
||||
private SteamwarWorld builderWorld(ServerVersion version, String map) {
|
||||
SteamwarWorld world = SteamwarWorld.getBuilderWorld(map, version.getVersionSuffix());
|
||||
if(world != null)
|
||||
return world;
|
||||
|
||||
File legacyWorld = mapFile(version, map);
|
||||
if(!legacyWorld.exists())
|
||||
return null;
|
||||
|
||||
return SteamwarWorld.getOrCreateBuilderWorld(map, version.getVersionSuffix(), legacyWorld);
|
||||
}
|
||||
|
||||
private File getWorldFolder(PreviousArguments previousArguments, int offset) {
|
||||
ServerVersion v = ServerVersion.get(previousArguments.userArgs[previousArguments.userArgs.length - offset]);
|
||||
if (v == null) return null;
|
||||
return new File(v.getWorldFolder(ServerStarter.BUILDER_BASE_PATH));
|
||||
private boolean mapExists(ServerVersion version, String map) {
|
||||
return SteamwarWorld.getBuilderWorld(map, version.getVersionSuffix()) != null || mapFile(version, map).exists();
|
||||
}
|
||||
|
||||
private File mapFile(ServerVersion version, String map) {
|
||||
return new File(version.getWorldFolder(ServerStarter.LEGACY_BUILDER_BASE_PATH), map);
|
||||
}
|
||||
|
||||
private ServerVersion getVersion(PreviousArguments previousArguments, int offset) {
|
||||
if (previousArguments.userArgs.length < offset) {
|
||||
return null;
|
||||
}
|
||||
return ServerVersion.get(previousArguments.userArgs[previousArguments.userArgs.length - offset]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -307,6 +307,13 @@ public class CheckCommand extends SWCommand {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove any added players from the schematic in the folder
|
||||
for (SchematicNode schematicNode : SchematicNode.getSchematicNodeInNode(teamFolder.getNodeId())) {
|
||||
for (NodeMember nodeMember : NodeMember.getNodeMembers(schematicNode.getNodeId())) {
|
||||
nodeMember.delete();
|
||||
}
|
||||
}
|
||||
|
||||
// Copy Schem into team folder of -1 user
|
||||
String name = DateTimeFormatter.ofPattern("yyyy.MM.dd_HH:mm:ss").format(schematic.getLastUpdate().toLocalDateTime());
|
||||
NodeData data = NodeData.getLatest(schematic);
|
||||
@@ -316,14 +323,8 @@ public class CheckCommand extends SWCommand {
|
||||
// Accept the team folder schematic and set other to Normal as well as adding the original owner on the schematic
|
||||
node.setSchemtype(GameModeConfig.getBySchematicType(schematic.getSchemtype()).Schematic.Type);
|
||||
NodeMember.createNodeMember(node.getNodeId(), schematic.getOwner());
|
||||
|
||||
// Remove any added players from the schematic in the folder
|
||||
for (SchematicNode schematicNode : SchematicNode.getSchematicNodeInNode(teamFolder.getNodeId())) {
|
||||
if (schematicNode.getNodeId() == node.getNodeId()) continue;
|
||||
for (NodeMember nodeMember : NodeMember.getNodeMembers(schematicNode.getNodeId())) {
|
||||
NodeMember.createNodeMember(node.getNodeId(), nodeMember.getMember());
|
||||
nodeMember.delete();
|
||||
}
|
||||
for (NodeMember nodeMember : NodeMember.getNodeMembers(schematic.getId())) {
|
||||
NodeMember.createNodeMember(node.getNodeId(), nodeMember.getMember());
|
||||
}
|
||||
|
||||
// Conclude by setting send in schematic to normal and broadcast
|
||||
|
||||
@@ -24,11 +24,16 @@ import de.steamwar.linkage.Linked;
|
||||
import de.steamwar.messages.Chatter;
|
||||
import de.steamwar.messages.PlayerChatter;
|
||||
import de.steamwar.sql.SteamwarUser;
|
||||
import de.steamwar.sql.SteamwarWorld;
|
||||
import de.steamwar.sql.UserPerm;
|
||||
import de.steamwar.sql.internal.Statement;
|
||||
import de.steamwar.velocitycore.ServerStarter;
|
||||
import de.steamwar.velocitycore.ServerVersion;
|
||||
import de.steamwar.velocitycore.VelocityCore;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
@@ -64,12 +69,21 @@ public class GDPRQuery extends SWCommand {
|
||||
copy(getClass().getClassLoader().getResourceAsStream("GDPRQueryREADME.md"), out, "README.txt");
|
||||
|
||||
sender.system("GDPR_STATUS_WORLD");
|
||||
copyBauwelt(user, out, "/home/minecraft/userworlds/" + user.getUUID().toString(), "BuildWorld12");
|
||||
copyBauwelt(user, out, "/home/minecraft/userworlds15/" + user.getId(), "BuildWorld15");
|
||||
Set<Integer> exportedVersions = new LinkedHashSet<>();
|
||||
for(ServerVersion version : ServerVersion.values()) {
|
||||
int versionSuffix = version.getVersionSuffix();
|
||||
if(!exportedVersions.add(versionSuffix))
|
||||
continue;
|
||||
|
||||
SteamwarWorld world = SteamwarWorld.getBauWorld(user, versionSuffix);
|
||||
if(world != null)
|
||||
copyBauwelt(user, out, world.setupAndGetStoragePath(), "BuildWorld" + versionSuffix);
|
||||
else
|
||||
copyLegacyBauwelt(user, out, version);
|
||||
}
|
||||
|
||||
sender.system("GDPR_STATUS_INVENTORIES");
|
||||
copyPlayerdata(user, out, "/home/minecraft/userworlds", "BuildInventories12");
|
||||
copyPlayerdata(user, out, "/home/minecraft/userworlds15", "BuildInventories15");
|
||||
copyPlayerdata(user, out, SteamwarWorld.WORLD_STORAGE, "BuildInventories");
|
||||
|
||||
sender.system("GDPR_STATUS_DATABASE");
|
||||
sqlCSV(user, out, bannedIPs, "BannedIPs.csv");
|
||||
@@ -99,8 +113,8 @@ public class GDPRQuery extends SWCommand {
|
||||
}
|
||||
|
||||
private static final Statement bannedIPs = new Statement("SELECT Timestamp, IP FROM BannedUserIPs WHERE UserID = ?");
|
||||
private static final Statement bauweltMember = new Statement("SELECT BauweltID AS Bauwelt, WorldEdit, World FROM BauweltMember WHERE MemberID = ?");
|
||||
private static final Statement bauweltMembers = new Statement("SELECT u.UserName AS 'User', m.WorldEdit AS WorldEdit, m.World AS World FROM BauweltMember m INNER JOIN UserData u ON m.MemberID = u.id WHERE m.BauweltID = ?");
|
||||
private static final Statement bauweltMember = new Statement("SELECT WorldID AS Bauwelt, WorldEdit, World FROM BauweltMember WHERE MemberID = ?");
|
||||
private static final Statement bauweltMembers = new Statement("SELECT w.Name AS World, u.UserName AS 'User', m.WorldEdit AS WorldEdit, m.World AS World FROM BauweltMember m INNER JOIN UserData u ON m.MemberID = u.id INNER JOIN world w ON m.WorldID = w.id WHERE w.Owner = ?");
|
||||
private static final Statement checkedSchems = new Statement("SELECT NodeName AS Schematic, StartTime, EndTime, DeclineReason AS Result FROM CheckedSchematic WHERE NodeOwner = ? ORDER BY StartTime ASC");
|
||||
private static final Statement fights = new Statement("SELECT p.Team AS Team, p.Kit AS Kit, p.Kills AS Kills, p.IsOut AS Died, f.GameMode AS GameMode, f.Server AS Server, f.StartTime AS StartTime, f.Duration AS Duration, (f.BlueLeader = p.UserID) AS IsBlueLeader, (f.RedLeader = p.UserID) AS IsRedLeader, f.Win AS Winner, f.WinCondition AS WinCondition FROM Fight f INNER JOIN FightPlayer p ON f.FightID = p.FightID WHERE p.UserID = ? ORDER BY StartTime ASC");
|
||||
private static final Statement ignoredPlayers = new Statement("SELECT u.UserName AS IgnoredPlayer FROM IgnoredPlayers i INNER JOIN UserData u ON i.Ignored = u.id WHERE Ignorer = ?");
|
||||
@@ -230,6 +244,18 @@ public class GDPRQuery extends SWCommand {
|
||||
}
|
||||
}
|
||||
|
||||
private void copyLegacyBauwelt(SteamwarUser user, ZipOutputStream out, ServerVersion version) throws IOException {
|
||||
File legacyIdWorld = new File(version.getWorldFolder(ServerStarter.LEGACY_WORLDS_BASE_PATH), String.valueOf(user.getId()));
|
||||
if(legacyIdWorld.exists()) {
|
||||
copyBauwelt(user, out, legacyIdWorld.getPath(), "BuildWorld" + version.getVersionSuffix());
|
||||
return;
|
||||
}
|
||||
|
||||
File legacyUuidWorld = new File(version.getWorldFolder(ServerStarter.LEGACY_WORLDS_BASE_PATH), user.getUUID().toString());
|
||||
if(legacyUuidWorld.exists())
|
||||
copyBauwelt(user, out, legacyUuidWorld.getPath(), "BuildWorld" + version.getVersionSuffix());
|
||||
}
|
||||
|
||||
private void copyPlayerdata(SteamwarUser user, ZipOutputStream out, String inDir, String outDir) throws IOException {
|
||||
File worlds = new File(inDir);
|
||||
String path = "playerdata/" + user.getUUID().toString() + ".dat";
|
||||
|
||||
@@ -42,6 +42,13 @@ public class ReplayCommand extends SWCommand {
|
||||
super("replay");
|
||||
}
|
||||
|
||||
@Register
|
||||
public void genericCommand(PlayerChatter sender) {
|
||||
sender.system("REPLAY_UNAVAILABLE");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
@Register
|
||||
public void genericCommand(PlayerChatter sender, int replayId, @StaticValue(value = {"", "-a"}, allowISE = true) @OptionalValue("") boolean isAdmin, @OptionalValue("") String optionalMap) {
|
||||
Fight fight = Fight.getById(replayId);
|
||||
@@ -109,4 +116,5 @@ public class ReplayCommand extends SWCommand {
|
||||
private Message parseLeader(SteamwarUser leader, int players, boolean winner) {
|
||||
return new Message("REPLAY_" + (players > 1 ? "" : "SOLO_") + (winner ? "WINNER" : "LOSER"), leader.getUserName(), players - 1);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import de.steamwar.messages.PlayerChatter;
|
||||
import de.steamwar.sql.*;
|
||||
import de.steamwar.velocitycore.VelocityCore;
|
||||
import de.steamwar.velocitycore.discord.DiscordBot;
|
||||
import de.steamwar.velocitycore.inventory.SWInventory;
|
||||
import de.steamwar.velocitycore.inventory.SWItem;
|
||||
import de.steamwar.velocitycore.inventory.SWListInv;
|
||||
import de.steamwar.velocitycore.util.SteamwarPrefix;
|
||||
@@ -437,6 +438,117 @@ public class TeamCommand extends SWCommand {
|
||||
}
|
||||
}
|
||||
|
||||
@Register("worlds")
|
||||
@Register("world")
|
||||
public void worlds(@Validator("isInTeam") PlayerChatter sender) {
|
||||
List<SWListInv.SWListEntry<SteamwarWorld>> entries = SteamwarWorld.getTeamWorlds(Team.byId(sender.user().getTeam())).stream()
|
||||
.map(world -> new SWListInv.SWListEntry<>(
|
||||
new SWItem("GRASS_BLOCK", new Message("PLAIN_STRING", "§e" + world.getName()))
|
||||
.addLore(new Message("PLAIN_STRING", "§7Version: §e" + world.getVersion()))
|
||||
.addLore(new Message("PLAIN_STRING", "§7Left: start, right: manage")),
|
||||
world
|
||||
))
|
||||
.toList();
|
||||
SWListInv<SteamwarWorld> inv = new SWListInv<>(sender, new Message("TEAM_WORLD_GUI_TITLE"), entries, (click, world) -> {
|
||||
if (click.isRightClick() && sender.user().isLeader()) {
|
||||
openTeamWorldManagement(sender, world);
|
||||
} else {
|
||||
startTeamWorld(sender, world);
|
||||
}
|
||||
});
|
||||
inv.open();
|
||||
}
|
||||
|
||||
@Register("world")
|
||||
public void world(@Validator("isInTeam") PlayerChatter sender, @Mapper("teamWorld") SteamwarWorld world) {
|
||||
startTeamWorld(sender, world);
|
||||
}
|
||||
|
||||
@Register(value = "world create", description = "TEAM_WORLD_CREATE_USAGE")
|
||||
public void createWorld(@Validator("isLeader") Chatter sender, String name, @ErrorMessage("BAU_WORLD_VERSION") ServerVersion version, @Mapper("templateWorld") SteamwarWorld template) {
|
||||
Team team = Team.byId(sender.user().getTeam());
|
||||
if (template == null || template.getVersion() != version.getVersionSuffix()) {
|
||||
sender.system("BAU_WORLD_UNKNOWN");
|
||||
return;
|
||||
}
|
||||
if (SteamwarWorld.getTeamWorld(team, name, version.getVersionSuffix()) != null) {
|
||||
sender.system("TEAM_WORLD_EXISTS");
|
||||
return;
|
||||
}
|
||||
long count = SteamwarWorld.countTeamWorlds(team);
|
||||
if (count > SteamwarWorld.TEAM_WORLD_LIMIT) {
|
||||
sender.system("TEAM_WORLD_OVER_LIMIT_DELETE", SteamwarWorld.TEAM_WORLD_LIMIT);
|
||||
return;
|
||||
}
|
||||
if (count >= SteamwarWorld.TEAM_WORLD_LIMIT) {
|
||||
sender.system("TEAM_WORLD_LIMIT", SteamwarWorld.TEAM_WORLD_LIMIT);
|
||||
return;
|
||||
}
|
||||
SteamwarWorld.getOrCreateTeamWorld(team, name, version.getVersionSuffix(), template.getStorageDirectory());
|
||||
sender.system("TEAM_WORLD_CREATED");
|
||||
}
|
||||
|
||||
@Register(value = "world rename", description = "TEAM_WORLD_RENAME_USAGE")
|
||||
public void renameWorld(@Validator("isLeader") Chatter sender, @Mapper("teamWorld") SteamwarWorld world, String newName) {
|
||||
Team team = Team.byId(sender.user().getTeam());
|
||||
if (hasTooManyTeamWorlds(sender, team)) return;
|
||||
if (SteamwarWorld.getTeamWorld(team, newName, world.getVersion()) != null) {
|
||||
sender.system("TEAM_WORLD_EXISTS");
|
||||
return;
|
||||
}
|
||||
world.rename(newName);
|
||||
sender.system("TEAM_WORLD_RENAMED");
|
||||
}
|
||||
|
||||
@Register(value = "world upgrade", description = "TEAM_WORLD_UPGRADE_USAGE")
|
||||
public void upgradeWorld(@Validator("isLeader") Chatter sender, @Mapper("teamWorld") SteamwarWorld world, @ErrorMessage("BAU_WORLD_VERSION") ServerVersion version) {
|
||||
if (hasTooManyTeamWorlds(sender, Team.byId(sender.user().getTeam()))) return;
|
||||
world.changeVersion(version.getVersionSuffix());
|
||||
sender.system("TEAM_WORLD_UPGRADED");
|
||||
}
|
||||
|
||||
@Register(value = "world delete", description = "TEAM_WORLD_DELETE_USAGE")
|
||||
public void deleteWorld(@Validator("isLeader") PlayerChatter sender, @Mapper("teamWorld") SteamwarWorld world) {
|
||||
SWInventory inventory = new SWInventory(sender, 9, new Message("BAU_DELETE_GUI_NAME"));
|
||||
inventory.addItem(0, new SWItem("LIME_DYE", new Message("BAU_DELETE_GUI_DELETE")), click -> {
|
||||
VelocityCore.schedule(() -> {
|
||||
Bauserver subserver = Bauserver.getByWorld(world.getUuid());
|
||||
if (subserver != null) {
|
||||
subserver.stop();
|
||||
}
|
||||
world.markDeleted();
|
||||
SubserverSystem.deleteFolder(VelocityCore.local, world.getStorageDirectory().getPath());
|
||||
sender.system("TEAM_WORLD_DELETED");
|
||||
}).schedule();
|
||||
inventory.close();
|
||||
});
|
||||
inventory.addItem(8, new SWItem("RED_DYE", new Message("BAU_DELETE_GUI_CANCEL")), click -> inventory.close());
|
||||
inventory.open();
|
||||
}
|
||||
|
||||
private void startTeamWorld(PlayerChatter sender, SteamwarWorld world) {
|
||||
new ServerStarter().build(ServerVersion.get(world.getVersion()), world).send(sender.getPlayer()).start();
|
||||
}
|
||||
|
||||
private void openTeamWorldManagement(PlayerChatter sender, SteamwarWorld world) {
|
||||
SWInventory inventory = new SWInventory(sender, 9, new Message("PLAIN_STRING", "§e" + world.getName()));
|
||||
inventory.addItem(0, new SWItem("ENDER_PEARL", new Message("PLAIN_STRING", "§aStart")), click -> {
|
||||
inventory.close();
|
||||
startTeamWorld(sender, world);
|
||||
});
|
||||
inventory.addItem(3, new SWItem("EXPERIENCE_BOTTLE", new Message("PLAIN_STRING", "§eUpgrade to latest")), click -> {
|
||||
if (hasTooManyTeamWorlds(sender, Team.byId(sender.user().getTeam()))) {
|
||||
inventory.close();
|
||||
return;
|
||||
}
|
||||
world.changeVersion(ServerVersion.PAPER_21.getVersionSuffix());
|
||||
sender.system("TEAM_WORLD_UPGRADED");
|
||||
inventory.close();
|
||||
});
|
||||
inventory.addItem(8, new SWItem("BARRIER", new Message("PLAIN_STRING", "§cDelete")), click -> deleteWorld(sender, world));
|
||||
inventory.open();
|
||||
}
|
||||
|
||||
@Register("event")
|
||||
public void event(@Validator("isLeader") Chatter sender, Event event) {
|
||||
Team team = Team.byId(sender.user().getTeam());
|
||||
@@ -585,6 +697,56 @@ public class TeamCommand extends SWCommand {
|
||||
};
|
||||
}
|
||||
|
||||
@Mapper(value = "teamWorld", local = true)
|
||||
public TypeMapper<SteamwarWorld> teamWorldMapper() {
|
||||
return new TypeMapper<>() {
|
||||
@Override
|
||||
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
Team team = Team.byId(sender.user().getTeam());
|
||||
return SteamwarWorld.getTeamWorlds(team).stream()
|
||||
.filter(world -> world.getName().equalsIgnoreCase(s) || world.getUuid().toString().equalsIgnoreCase(s))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
if (sender.user().getTeam() == 0) return List.of();
|
||||
return SteamwarWorld.getTeamWorlds(Team.byId(sender.user().getTeam())).stream()
|
||||
.map(SteamwarWorld::getName)
|
||||
.toList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Mapper(value = "templateWorld", local = true)
|
||||
public TypeMapper<SteamwarWorld> templateWorldMapper() {
|
||||
return new TypeMapper<>() {
|
||||
@Override
|
||||
public SteamwarWorld map(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
|
||||
if (version == null) return null;
|
||||
return SteamwarWorld.getTemplateWorld(s, version.getVersionSuffix());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> tabCompletes(Chatter sender, PreviousArguments previousArguments, String s) {
|
||||
ServerVersion version = previousArguments.getAll(ServerVersion.class).stream().findFirst().orElse(null);
|
||||
if (version == null) return List.of();
|
||||
return SteamwarWorld.getTemplateWorlds(version.getVersionSuffix()).stream().map(SteamwarWorld::getName).toList();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private boolean hasTooManyTeamWorlds(Chatter sender, Team team) {
|
||||
if (SteamwarWorld.countTeamWorlds(team) <= SteamwarWorld.TEAM_WORLD_LIMIT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
sender.system("TEAM_WORLD_OVER_LIMIT_DELETE", SteamwarWorld.TEAM_WORLD_LIMIT);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean checkTeamName(Chatter sender, Team team, String arg) {
|
||||
Team t = Team.get(arg);
|
||||
if (t != null && t.getTeamId() != team.getTeamId()) {
|
||||
|
||||
@@ -120,17 +120,26 @@ public class TpCommand extends SWCommand {
|
||||
}
|
||||
} else if (Subserver.isBuild(subserver)) {
|
||||
Bauserver bauserver = (Bauserver) subserver;
|
||||
SteamwarWorld world = SteamwarWorld.getWorld(bauserver.getWorld());
|
||||
if (world != null && world.getType() == WorldType.TEAM) {
|
||||
if (sender.user().getTeam() != world.getTeam().getValue()) {
|
||||
sender.system("JOIN_PLAYER_BLOCK");
|
||||
return;
|
||||
}
|
||||
SubserverSystem.sendPlayer(subserver, sender.getPlayer());
|
||||
return;
|
||||
}
|
||||
Player checker = VelocityCore.getProxy().getPlayer(bauserver.getOwner()).orElse(null);
|
||||
if (checker != null && CheckCommand.isChecking(checker)) {
|
||||
if (!sender.user().hasPerm(UserPerm.CHECK) && CheckCommand.getCheckingSchem(checker).getOwner() != sender.user().getId()) {
|
||||
sender.system("JOIN_PLAYER_BLOCK");
|
||||
return;
|
||||
}
|
||||
} else if (BauLock.isLocked(SteamwarUser.get(bauserver.getOwner()), sender.user())) {
|
||||
} else if (world != null && BauLock.isLocked(world, sender.user())) {
|
||||
sender.system("BAU_LOCKED_NOALLOWED");
|
||||
Chatter.of(bauserver.getOwner()).system("BAU_LOCK_BLOCKED", sender);
|
||||
return;
|
||||
} else if (!bauserver.getOwner().equals(sender.user().getUUID()) && BauweltMember.getBauMember(bauserver.getOwner(), sender.user().getUUID()) == null) {
|
||||
} else if (!bauserver.getOwner().equals(sender.user().getUUID()) && BauweltMember.getBauMember(bauserver.getWorld(), sender.user().getUUID()) == null) {
|
||||
SubserverSystem.sendDeniedMessage(sender, bauserver.getOwner());
|
||||
sender.system("JOIN_PLAYER_BLOCK");
|
||||
return;
|
||||
|
||||
@@ -95,6 +95,7 @@ public class CouncilChannel extends StaticMessageChannel {
|
||||
super(threadChannel, () -> {
|
||||
MessageCreateBuilder messageCreateBuilder = new MessageCreateBuilder();
|
||||
messageCreateBuilder.setContent("# Ratsmitglieder");
|
||||
Set<String> uniqueNames = new HashSet<>();
|
||||
membersByRole.get(role)
|
||||
.stream()
|
||||
.map(member -> {
|
||||
@@ -105,6 +106,7 @@ public class CouncilChannel extends StaticMessageChannel {
|
||||
})
|
||||
.sorted(Map.Entry.comparingByKey())
|
||||
.forEach(entry -> {
|
||||
if (!uniqueNames.add(entry.getKey())) return;
|
||||
messageCreateBuilder.addEmbeds(new EmbedBuilder()
|
||||
.setTitle(entry.getKey())
|
||||
.setImage(entry.getValue() == null ? null : "https://api.steamwar.de/data/skin/" + entry.getValue())
|
||||
|
||||
@@ -25,37 +25,39 @@ import de.steamwar.network.packets.server.BaulockUpdatePacket;
|
||||
import de.steamwar.persistent.Bauserver;
|
||||
import de.steamwar.sql.BauweltMember;
|
||||
import de.steamwar.sql.SteamwarUser;
|
||||
import de.steamwar.sql.UserConfig;
|
||||
import de.steamwar.sql.SteamwarWorld;
|
||||
import de.steamwar.sql.UserPerm;
|
||||
import de.steamwar.sql.WorldType;
|
||||
import de.steamwar.velocitycore.network.NetworkSender;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class BauLock {
|
||||
|
||||
private static final String BAU_LOCK_CONFIG_NAME = "baulockstate";
|
||||
|
||||
public static void setLocked(Chatter owner, BauLockState state) {
|
||||
UserConfig.updatePlayerConfig(owner.user().getId(), BAU_LOCK_CONFIG_NAME, state == BauLockState.OPEN ? null : state.name());
|
||||
public static void setLocked(Chatter owner, SteamwarWorld world, BauLockState state) {
|
||||
world.changeLockState(state == BauLockState.OPEN ? null : state.name());
|
||||
owner.system("BAU_LOCKED_" + state.name());
|
||||
|
||||
Bauserver bauserver = Bauserver.get(owner.user().getUUID());
|
||||
Bauserver bauserver = Bauserver.getByWorld(world.getUuid());
|
||||
if (bauserver != null) {
|
||||
bauserver.getRegisteredServer().getPlayersConnected().stream().findAny().ifPresent(player -> NetworkSender.send(player, new BaulockUpdatePacket()));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLocked(SteamwarUser owner, SteamwarUser target) {
|
||||
if (owner.getId() == target.getId()) return false;
|
||||
public static boolean isLocked(SteamwarWorld world, SteamwarUser target) {
|
||||
if (world.getType() != WorldType.BAU || world.getOwner() == null) return false;
|
||||
|
||||
SteamwarUser owner = SteamwarUser.byId(world.getOwner().getValue());
|
||||
if (owner == null || owner.getId() == target.getId()) return false;
|
||||
|
||||
boolean locked;
|
||||
String state = UserConfig.getConfig(owner.getId(), BAU_LOCK_CONFIG_NAME);
|
||||
String state = world.getLockState();
|
||||
switch (state == null ? BauLockState.OPEN : BauLockState.valueOf(state)) {
|
||||
case NOBODY:
|
||||
locked = true;
|
||||
break;
|
||||
case SUPERVISOR:
|
||||
BauweltMember member = BauweltMember.getBauMember(owner.getId(), target.getId());
|
||||
BauweltMember member = BauweltMember.getBauMember(world.getUuid(), target.getId());
|
||||
locked = member == null || !member.isSupervisor();
|
||||
break;
|
||||
case SERVERTEAM:
|
||||
|
||||
Reference in New Issue
Block a user