Skip to content

Commit ba72127

Browse files
committed
edit module initial commit
1 parent 5f31df6 commit ba72127

7 files changed

Lines changed: 536 additions & 0 deletions

File tree

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package server.modules.edit;
2+
3+
public class BaseWorldEditConfiig implements WorldEditConfig {
4+
5+
@Override
6+
public int getMaxCircleRadius() {
7+
return 1000;
8+
}
9+
10+
@Override
11+
public int getMaxDiscRadius() {
12+
return 100;
13+
}
14+
15+
@Override
16+
public int getMaxSphereRadius() {
17+
return 100;
18+
}
19+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package server.modules.edit;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import java.util.HashSet;
6+
import java.util.List;
7+
import java.util.Map;
8+
import java.util.Set;
9+
import java.util.concurrent.ExecutorService;
10+
import java.util.concurrent.Executors;
11+
12+
import common.network.packets.ChunkDataPacket;
13+
import common.world.ChunkData;
14+
import common.world.WorldMath;
15+
import server.network.GameServer;
16+
import server.player.ServerPlayer;
17+
import server.world.ServerWorld;
18+
19+
public class ChunkTransaction {
20+
21+
private final Map<Long, List<BlockChange>> changes = new HashMap<>();
22+
23+
// 🔥 Async Network Thread Pool
24+
private static final ExecutorService NETWORK_EXECUTOR = Executors.newFixedThreadPool(2);
25+
26+
public record BlockChange(int x, int y, int z, short id) {}
27+
28+
private long key(int cx, int cz) {
29+
return (((long) cx) << 32) | (cz & 0xffffffffL);
30+
}
31+
32+
public void setBlock(int x, int y, int z, short id) {
33+
int cx = WorldMath.worldToChunk(x, ChunkData.WIDTH);
34+
int cz = WorldMath.worldToChunk(z, ChunkData.DEPTH);
35+
36+
long key = key(cx, cz);
37+
38+
changes.computeIfAbsent(key, k -> new ArrayList<>()).add(new BlockChange(x, y, z, id));
39+
}
40+
41+
public void commit(ServerWorld world, GameServer server) {
42+
43+
Set<ChunkData> touchedChunks = new HashSet<>();
44+
45+
// =========================
46+
// 1. APPLY CHANGES (SYNC)
47+
// =========================
48+
for (Map.Entry<Long, List<BlockChange>> entry : changes.entrySet()) {
49+
50+
long key = entry.getKey();
51+
52+
int cx = (int) (key >> 32);
53+
int cz = (int) (key & 0xffffffffL);
54+
55+
ChunkData chunk = world.getOrCreateChunk(cx, cz);
56+
if (chunk == null) continue;
57+
58+
for (BlockChange change : entry.getValue()) {
59+
60+
int bx = change.x() - (cx * ChunkData.WIDTH);
61+
int bz = change.z() - (cz * ChunkData.DEPTH);
62+
int by = change.y();
63+
64+
if (by < 0 || by >= ChunkData.HEIGHT) continue;
65+
66+
chunk.setBlockId(change.id(), bx, by, bz);
67+
}
68+
69+
chunk.setDirty(true);
70+
touchedChunks.add(chunk);
71+
}
72+
73+
// 🔥 Player Snapshot (thread-safe)
74+
List<ServerPlayer> players = new ArrayList<>(server.getPlayerManager().getAllPlayers());
75+
76+
// =========================
77+
// 2. SEND CHUNKS (ASYNC)
78+
// =========================
79+
for (ChunkData chunk : touchedChunks) {
80+
81+
ChunkDataPacket packet = new ChunkDataPacket(chunk);
82+
83+
NETWORK_EXECUTOR.submit(
84+
() -> {
85+
for (ServerPlayer player : players) {
86+
87+
int dx = Math.abs(player.getChunkX() - chunk.getChunkX());
88+
int dz = Math.abs(player.getChunkZ() - chunk.getChunkZ());
89+
90+
if (dx <= 8 && dz <= 8) {
91+
92+
// ❗ falls connection nicht thread-safe ist:
93+
synchronized (player.getConnection()) {
94+
player.getConnection().send(packet);
95+
}
96+
}
97+
}
98+
});
99+
}
100+
101+
// =========================
102+
// 3. NEIGHBOR UPDATES (ASYNC)
103+
// =========================
104+
for (ChunkData chunk : touchedChunks) {
105+
106+
int cx = chunk.getChunkX();
107+
int cz = chunk.getChunkZ();
108+
109+
int[][] offsets = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
110+
111+
for (int[] off : offsets) {
112+
113+
ChunkData neighbor = world.getOrCreateChunk(cx + off[0], cz + off[1]);
114+
if (neighbor == null) continue;
115+
116+
ChunkDataPacket packet = new ChunkDataPacket(neighbor);
117+
118+
NETWORK_EXECUTOR.submit(
119+
() -> {
120+
for (ServerPlayer player : players) {
121+
122+
int dx = Math.abs(player.getChunkX() - neighbor.getChunkX());
123+
int dz = Math.abs(player.getChunkZ() - neighbor.getChunkZ());
124+
125+
if (dx <= 8 && dz <= 8) {
126+
127+
synchronized (player.getConnection()) {
128+
player.getConnection().send(packet);
129+
}
130+
}
131+
}
132+
});
133+
}
134+
}
135+
136+
// optional: clear changes nach commit
137+
changes.clear();
138+
}
139+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package server.modules.edit;
2+
3+
import java.util.Collection;
4+
import java.util.List;
5+
6+
import common.game.block.BlockRegistry;
7+
import common.game.block.BlockType;
8+
import common.world.Location;
9+
import server.commands.AbstractCommand;
10+
import server.commands.CommandArgument;
11+
import server.commands.CommandContext;
12+
import server.gateways.PlayerGateway;
13+
import voxels.Voxel;
14+
import voxels.VoxelCircleCreator;
15+
16+
public class CircleCommand extends AbstractCommand {
17+
18+
private final PlayerGateway players;
19+
20+
private final WorldEditConfig config;
21+
22+
public CircleCommand(PlayerGateway players, WorldEditConfig config) {
23+
this.players = players;
24+
this.config = config;
25+
}
26+
27+
@Override
28+
public String getPermission() {
29+
return "";
30+
}
31+
32+
@Override
33+
public void execute(CommandContext ctx) {
34+
Location location = players.getLocation(ctx.getPlayer());
35+
36+
List<String> args = ctx.getArgs();
37+
38+
if (args.size() < 1) {
39+
ctx.reply("Missing argument: radius");
40+
return;
41+
}
42+
43+
if (args.size() < 2) {
44+
ctx.reply("Missing argument: block_type");
45+
return;
46+
}
47+
48+
int radius;
49+
50+
try {
51+
radius = Integer.parseInt(args.get(0));
52+
} catch (NumberFormatException e) {
53+
ctx.reply("An error occured: Wrong number format.");
54+
return;
55+
}
56+
57+
if (radius > config.getMaxCircleRadius()) {
58+
ctx.reply("Max circle radius: " + config.getMaxCircleRadius());
59+
return;
60+
}
61+
62+
String name = args.get(1);
63+
BlockType blockType = BlockRegistry.get(name);
64+
if (blockType == null) {
65+
ctx.reply("Unknown block type: " + name);
66+
return;
67+
}
68+
short id = blockType.getId();
69+
70+
ctx.reply("Start creating...");
71+
72+
int bx = location.getBlockX();
73+
int by = location.getBlockY();
74+
int bz = location.getBlockZ();
75+
76+
Voxel center = new Voxel(bx, by, bz);
77+
VoxelCircleCreator creator = new VoxelCircleCreator(center, radius);
78+
79+
Collection<Voxel> voxels = creator.create();
80+
81+
ChunkTransaction tx = new ChunkTransaction();
82+
83+
for (Voxel v : voxels) {
84+
tx.setBlock(v.x(), v.y(), v.z(), id);
85+
}
86+
87+
tx.commit(ctx.getServer().getWorld(), ctx.getServer());
88+
89+
ctx.reply("Circle created with " + voxels.size() + " blocks.");
90+
}
91+
92+
@Override
93+
public String getName() {
94+
return "/circle";
95+
}
96+
97+
@Override
98+
public CommandArgument[] getArgumentLabels() {
99+
return new CommandArgument[] {
100+
new CommandArgument("radius", true),
101+
new CommandArgument("block_type", true)
102+
};
103+
}
104+
105+
@Override
106+
public String getDescription() {
107+
return "";
108+
}
109+
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package server.modules.edit;
2+
3+
import java.util.Collection;
4+
import java.util.List;
5+
6+
import common.game.block.BlockRegistry;
7+
import common.game.block.BlockType;
8+
import common.world.Location;
9+
import server.commands.AbstractCommand;
10+
import server.commands.CommandArgument;
11+
import server.commands.CommandContext;
12+
import server.gateways.PlayerGateway;
13+
import voxels.Voxel;
14+
import voxels.VoxelDiscCreator;
15+
16+
public class DiscCommand extends AbstractCommand {
17+
18+
private final PlayerGateway players;
19+
20+
private final WorldEditConfig config;
21+
22+
public DiscCommand(PlayerGateway players, WorldEditConfig config) {
23+
this.players = players;
24+
this.config = config;
25+
}
26+
27+
@Override
28+
public String getPermission() {
29+
return "";
30+
}
31+
32+
@Override
33+
public void execute(CommandContext ctx) {
34+
Location location = players.getLocation(ctx.getPlayer());
35+
36+
List<String> args = ctx.getArgs();
37+
38+
if (args.size() < 1) {
39+
ctx.reply("Missing argument: radius");
40+
return;
41+
}
42+
43+
if (args.size() < 2) {
44+
ctx.reply("Missing argument: block_type");
45+
return;
46+
}
47+
48+
int radius;
49+
50+
try {
51+
radius = Integer.parseInt(args.get(0));
52+
} catch (NumberFormatException e) {
53+
ctx.reply("An error occured: Wrong number format.");
54+
return;
55+
}
56+
57+
if (radius > config.getMaxCircleRadius()) {
58+
ctx.reply("Max circle radius: " + config.getMaxCircleRadius());
59+
return;
60+
}
61+
62+
String name = args.get(1);
63+
BlockType blockType = BlockRegistry.get(name);
64+
if (blockType == null) {
65+
ctx.reply("Unknown block type: " + name);
66+
return;
67+
}
68+
short id = blockType.getId();
69+
70+
ctx.reply("Start creating...");
71+
72+
int bx = location.getBlockX();
73+
int by = location.getBlockY();
74+
int bz = location.getBlockZ();
75+
76+
Voxel center = new Voxel(bx, by, bz);
77+
VoxelDiscCreator creator = new VoxelDiscCreator(center, radius);
78+
79+
Collection<Voxel> voxels = creator.create();
80+
81+
ChunkTransaction tx = new ChunkTransaction();
82+
83+
for (Voxel v : voxels) {
84+
tx.setBlock(v.x(), v.y(), v.z(), id);
85+
}
86+
87+
tx.commit(ctx.getServer().getWorld(), ctx.getServer());
88+
89+
ctx.reply("Disc created with " + voxels.size() + " blocks.");
90+
}
91+
92+
@Override
93+
public String getName() {
94+
return "/disc";
95+
}
96+
97+
@Override
98+
public CommandArgument[] getArgumentLabels() {
99+
return new CommandArgument[] {
100+
new CommandArgument("radius", true),
101+
new CommandArgument("block_type", true)
102+
};
103+
}
104+
105+
@Override
106+
public String getDescription() {
107+
return "";
108+
}
109+
}

0 commit comments

Comments
 (0)