Dumped server ticking code into the Mariana Trench and rewritten it
No regrets. - Pretty much everything related to ticking changed - Ticking is now capable of running in parallel (other code - not so much) - Implemented world updates - Broke some stuff
This commit is contained in:
parent
6d6e0f6ca4
commit
44de6a86f7
@ -1,7 +1,6 @@
|
|||||||
package ru.windcorp.progressia.client.audio.backend;
|
package ru.windcorp.progressia.client.audio.backend;
|
||||||
|
|
||||||
import org.lwjgl.BufferUtils;
|
import org.lwjgl.BufferUtils;
|
||||||
import ru.windcorp.progressia.client.audio.backend.SoundType;
|
|
||||||
import ru.windcorp.progressia.common.resource.*;
|
import ru.windcorp.progressia.common.resource.*;
|
||||||
|
|
||||||
import java.nio.IntBuffer;
|
import java.nio.IntBuffer;
|
||||||
|
@ -26,15 +26,6 @@ public class SoundType extends Namespaced {
|
|||||||
alBufferData(audioBuffer, format, rawAudio, sampleRate);
|
alBufferData(audioBuffer, format, rawAudio, sampleRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO What is this (Eugene Smirnov)
|
|
||||||
private Speaker createSound(int source, int audio) {
|
|
||||||
if (!alIsBuffer(audio) || !alIsSource(source))
|
|
||||||
throw new RuntimeException();
|
|
||||||
|
|
||||||
alBufferData(audio, format, rawAudio, sampleRate);
|
|
||||||
return new Speaker(audio);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initSpeaker(Speaker speaker) {
|
public void initSpeaker(Speaker speaker) {
|
||||||
speaker.setAudioData(audioBuffer);
|
speaker.setAudioData(audioBuffer);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package ru.windcorp.progressia.common.util.crash.providers;
|
package ru.windcorp.progressia.common.util.crash.providers;
|
||||||
|
|
||||||
import ru.windcorp.progressia.Progressia;
|
|
||||||
import ru.windcorp.progressia.ProgressiaLauncher;
|
import ru.windcorp.progressia.ProgressiaLauncher;
|
||||||
import ru.windcorp.progressia.common.util.crash.ContextProvider;
|
import ru.windcorp.progressia.common.util.crash.ContextProvider;
|
||||||
|
|
||||||
|
@ -210,7 +210,11 @@ public class ChunkData {
|
|||||||
private void createNormalTileContainer(Vec3i blockInChunk, BlockFace face) {
|
private void createNormalTileContainer(Vec3i blockInChunk, BlockFace face) {
|
||||||
List<TileData> primaryList =
|
List<TileData> primaryList =
|
||||||
SizeLimitedList.wrap(
|
SizeLimitedList.wrap(
|
||||||
new ArrayList<>(TILES_PER_FACE), TILES_PER_FACE
|
new ChunkDataReportingList(
|
||||||
|
new ArrayList<>(TILES_PER_FACE),
|
||||||
|
this, blockInChunk, face
|
||||||
|
),
|
||||||
|
TILES_PER_FACE
|
||||||
);
|
);
|
||||||
|
|
||||||
List<TileData> secondaryList = Lists.reverse(primaryList);
|
List<TileData> secondaryList = Lists.reverse(primaryList);
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
package ru.windcorp.progressia.common.world;
|
||||||
|
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
|
|
||||||
|
class ChunkDataReportingList extends AbstractList<TileData> {
|
||||||
|
|
||||||
|
private final List<TileData> parent;
|
||||||
|
private final ChunkData reportTo;
|
||||||
|
private final Vec3i blockInChunk;
|
||||||
|
private final BlockFace face;
|
||||||
|
|
||||||
|
public ChunkDataReportingList(List<TileData> parent, ChunkData reportTo, Vec3i blockInChunk, BlockFace face) {
|
||||||
|
super();
|
||||||
|
this.parent = parent;
|
||||||
|
this.reportTo = reportTo;
|
||||||
|
this.blockInChunk = new Vec3i(blockInChunk);
|
||||||
|
this.face = face;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TileData get(int index) {
|
||||||
|
return parent.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return parent.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TileData set(int index, TileData element) {
|
||||||
|
TileData previous = parent.set(index, element);
|
||||||
|
report(previous, element);
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int index, TileData element) {
|
||||||
|
parent.add(index, element);
|
||||||
|
report(null, element);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TileData remove(int index) {
|
||||||
|
TileData previous = parent.remove(index);
|
||||||
|
report(previous, null);
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void report(TileData previous, TileData current) {
|
||||||
|
reportTo.getListeners().forEach(l -> {
|
||||||
|
if (previous != null) {
|
||||||
|
l.onChunkTilesChanged(reportTo, blockInChunk, face, previous, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current != null) {
|
||||||
|
l.onChunkTilesChanged(reportTo, blockInChunk, face, current, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
l.onChunkChanged(reportTo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -120,6 +120,15 @@ public final class BlockFace extends BlockRelation {
|
|||||||
return counterFace;
|
return counterFace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public BlockFace getCounter() {
|
||||||
|
return counterFace;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlockFace getCounterAndMoveCursor(Vec3i cursor) {
|
||||||
|
cursor.add(getVector());
|
||||||
|
return counterFace;
|
||||||
|
}
|
||||||
|
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
package ru.windcorp.progressia.server;
|
package ru.windcorp.progressia.server;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import ru.windcorp.jputil.functions.ThrowingRunnable;
|
import ru.windcorp.jputil.functions.ThrowingRunnable;
|
||||||
import ru.windcorp.progressia.common.util.TaskQueue;
|
import ru.windcorp.progressia.common.util.TaskQueue;
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
import ru.windcorp.progressia.server.comms.ClientManager;
|
import ru.windcorp.progressia.server.comms.ClientManager;
|
||||||
import ru.windcorp.progressia.server.world.Changer;
|
|
||||||
import ru.windcorp.progressia.server.world.ImplementedChangeTracker;
|
|
||||||
import ru.windcorp.progressia.server.world.WorldLogic;
|
import ru.windcorp.progressia.server.world.WorldLogic;
|
||||||
|
import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
|
||||||
|
import ru.windcorp.progressia.server.world.ticking.Change;
|
||||||
|
import ru.windcorp.progressia.server.world.ticking.Evaluation;
|
||||||
|
|
||||||
public class Server {
|
public class Server {
|
||||||
|
|
||||||
@ -15,18 +21,20 @@ public class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final WorldLogic world;
|
private final WorldLogic world;
|
||||||
private final ImplementedChangeTracker adHocChanger =
|
private final WorldAccessor worldAccessor = new WorldAccessor(this);
|
||||||
new ImplementedChangeTracker();
|
|
||||||
|
|
||||||
private final ServerThread serverThread;
|
private final ServerThread serverThread;
|
||||||
|
|
||||||
private final ClientManager clientManager = new ClientManager(this);
|
private final ClientManager clientManager = new ClientManager(this);
|
||||||
|
|
||||||
private final TaskQueue taskQueue = new TaskQueue(this::isServerThread);
|
private final TaskQueue taskQueue = new TaskQueue(this::isServerThread);
|
||||||
|
private final Collection<Consumer<Server>> repeatingTasks = Collections.synchronizedCollection(new ArrayList<>());
|
||||||
|
|
||||||
public Server(WorldData world) {
|
public Server(WorldData world) {
|
||||||
this.world = new WorldLogic(world);
|
this.world = new WorldLogic(world, this);
|
||||||
this.serverThread = new ServerThread(this);
|
this.serverThread = new ServerThread(this);
|
||||||
|
|
||||||
|
invokeEveryTick(this::scheduleChunkTicks);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldLogic getWorld() {
|
public WorldLogic getWorld() {
|
||||||
@ -34,24 +42,52 @@ public class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do not use in ticks
|
* Returns this server's {@link ClientManager}.
|
||||||
|
* Use this to deal with communications, e.g. send packets.
|
||||||
|
* @return the {@link ClientManager} that handles this server
|
||||||
*/
|
*/
|
||||||
public Changer getAdHocChanger() {
|
|
||||||
return adHocChanger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientManager getClientManager() {
|
public ClientManager getClientManager() {
|
||||||
return clientManager;
|
return clientManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this thread is the main thread of this server.
|
||||||
|
* @return {@code true} iff the invocation occurs in server main thread
|
||||||
|
*/
|
||||||
public boolean isServerThread() {
|
public boolean isServerThread() {
|
||||||
return getCurrentServer() == this;
|
return getCurrentServer() == this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests that the provided task is executed once on next server tick.
|
||||||
|
* The task will be run in the main server thread. The task object is
|
||||||
|
* discarded after execution.
|
||||||
|
*
|
||||||
|
* <p>Use this method to request a one-time (rare) action that must necessarily
|
||||||
|
* happen in the main server thread, such as initialization tasks or reconfiguration.
|
||||||
|
*
|
||||||
|
* @param task the task to run
|
||||||
|
* @see #invokeNow(Runnable)
|
||||||
|
* @see #invokeEveryTick(Consumer)
|
||||||
|
*/
|
||||||
public void invokeLater(Runnable task) {
|
public void invokeLater(Runnable task) {
|
||||||
taskQueue.invokeLater(task);
|
taskQueue.invokeLater(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the tasks in the server main thread as soon as possible.
|
||||||
|
*
|
||||||
|
* <p>If this method is invoked in the server main thread, then the task is
|
||||||
|
* run immediately (the method blocks until the task finishes). Otherwise
|
||||||
|
* this method behaves exactly like {@link #invokeLater(Runnable)}.
|
||||||
|
*
|
||||||
|
* <p>Use this method to make sure that a piece of code is run in the main server
|
||||||
|
* thread.
|
||||||
|
*
|
||||||
|
* @param task the task to run
|
||||||
|
* @see #invokeLater(Runnable)
|
||||||
|
* @see #invokeEveryTick(Consumer)
|
||||||
|
*/
|
||||||
public void invokeNow(Runnable task) {
|
public void invokeNow(Runnable task) {
|
||||||
taskQueue.invokeNow(task);
|
taskQueue.invokeNow(task);
|
||||||
}
|
}
|
||||||
@ -62,17 +98,41 @@ public class Server {
|
|||||||
taskQueue.waitAndInvoke(task);
|
taskQueue.waitAndInvoke(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void invokeEveryTick(Consumer<Server> task) {
|
||||||
|
repeatingTasks.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestChange(Change change) {
|
||||||
|
serverThread.getTicker().requestChange(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void requestEvaluation(Evaluation evaluation) {
|
||||||
|
serverThread.getTicker().requestEvaluation(evaluation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTickLength() {
|
||||||
|
return this.serverThread.getTicker().getTickLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WorldAccessor getWorldAccessor() {
|
||||||
|
return worldAccessor;
|
||||||
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
this.serverThread.start();
|
this.serverThread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tick() {
|
public void tick() {
|
||||||
taskQueue.runTasks();
|
taskQueue.runTasks();
|
||||||
adHocChanger.applyChanges(this);
|
repeatingTasks.forEach(t -> t.accept(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown(String message) {
|
public void shutdown(String message) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void scheduleChunkTicks(Server server) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,9 @@ package ru.windcorp.progressia.server;
|
|||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
|
||||||
import ru.windcorp.progressia.server.world.Ticker;
|
import ru.windcorp.progressia.server.world.ticking.TickerCoordinator;
|
||||||
|
|
||||||
public class ServerThread implements Runnable {
|
public class ServerThread implements Runnable {
|
||||||
|
|
||||||
@ -39,14 +38,15 @@ public class ServerThread implements Runnable {
|
|||||||
r -> new Thread(new ServerThreadTracker(r), "Server thread")
|
r -> new Thread(new ServerThreadTracker(r), "Server thread")
|
||||||
);
|
);
|
||||||
|
|
||||||
private final Ticker ticker;
|
private final TickerCoordinator ticker;
|
||||||
|
|
||||||
public ServerThread(Server server) {
|
public ServerThread(Server server) {
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.ticker = new Ticker(server);
|
this.ticker = new TickerCoordinator(server, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
|
ticker.start();
|
||||||
executor.scheduleAtFixedRate(this, 0, 1000 / 20, TimeUnit.MILLISECONDS);
|
executor.scheduleAtFixedRate(this, 0, 1000 / 20, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ public class ServerThread implements Runnable {
|
|||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
server.tick();
|
server.tick();
|
||||||
ticker.run();
|
ticker.runOneTick();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LogManager.getLogger(getClass()).error("Got an exception in server thread", e);
|
LogManager.getLogger(getClass()).error("Got an exception in server thread", e);
|
||||||
}
|
}
|
||||||
@ -64,4 +64,8 @@ public class ServerThread implements Runnable {
|
|||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TickerCoordinator getTicker() {
|
||||||
|
return ticker;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,24 +0,0 @@
|
|||||||
package ru.windcorp.progressia.server.world;
|
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
|
||||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
|
||||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
|
||||||
|
|
||||||
public interface Changer {
|
|
||||||
|
|
||||||
@FunctionalInterface
|
|
||||||
public static interface Change<T> {
|
|
||||||
void change(T object);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setBlock(Vec3i pos, BlockData block);
|
|
||||||
|
|
||||||
void addTile(Vec3i block, BlockFace face, TileData tile);
|
|
||||||
|
|
||||||
void removeTile(Vec3i block, BlockFace face, TileData tile);
|
|
||||||
|
|
||||||
<T extends EntityData> void changeEntity(T entity, Change<T> change);
|
|
||||||
|
|
||||||
}
|
|
@ -12,7 +12,9 @@ import com.google.common.collect.Lists;
|
|||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.client.world.tile.TileLocation;
|
import ru.windcorp.progressia.client.world.tile.TileLocation;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.Coordinates;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
||||||
@ -47,34 +49,33 @@ public class ChunkLogic {
|
|||||||
MutableTileTickContext tileTickContext =
|
MutableTileTickContext tileTickContext =
|
||||||
new MutableTileTickContext();
|
new MutableTileTickContext();
|
||||||
|
|
||||||
blockTickContext.setWorld(getWorld());
|
|
||||||
blockTickContext.setChunk(this);
|
|
||||||
|
|
||||||
tileTickContext.setWorld(getWorld());
|
|
||||||
tileTickContext.setChunk(this);
|
|
||||||
|
|
||||||
data.forEachBlock(blockInChunk -> {
|
data.forEachBlock(blockInChunk -> {
|
||||||
BlockLogic block = getBlock(blockInChunk);
|
BlockLogic block = getBlock(blockInChunk);
|
||||||
|
|
||||||
if (block instanceof TickableBlock) {
|
if (block instanceof TickableBlock) {
|
||||||
blockTickContext.setCoordsInChunk(blockInChunk);
|
Vec3i blockInWorld = Vectors.grab3i();
|
||||||
|
Coordinates.getInWorld(getData().getPosition(), blockInChunk, blockInWorld);
|
||||||
|
|
||||||
if (((TickableBlock) block)
|
blockTickContext.init(getWorld().getServer(), blockInWorld);
|
||||||
.doesTickRegularly(blockTickContext)
|
|
||||||
) {
|
Vectors.release(blockInWorld);
|
||||||
|
|
||||||
|
if (((TickableBlock) block).doesTickRegularly(blockTickContext)) {
|
||||||
tickingBlocks.add(new Vec3i(blockInChunk));
|
tickingBlocks.add(new Vec3i(blockInChunk));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
data.forEachTile((loc, tileData) -> {
|
data.forEachTile((loc, tileData) -> {
|
||||||
TileLogic tile =
|
TileLogic tile = TileLogicRegistry.getInstance().get(tileData.getId());
|
||||||
TileLogicRegistry.getInstance().get(tileData.getId());
|
|
||||||
|
|
||||||
if (tile instanceof TickableTile) {
|
if (tile instanceof TickableTile) {
|
||||||
tileTickContext.setCoordsInChunk(loc.pos);
|
Vec3i blockInWorld = Vectors.grab3i();
|
||||||
tileTickContext.setFace(loc.face);
|
Coordinates.getInWorld(getData().getPosition(), loc.pos, blockInWorld);
|
||||||
tileTickContext.setLayer(loc.layer);
|
|
||||||
|
tileTickContext.init(getWorld().getServer(), blockInWorld, loc.face, loc.layer);
|
||||||
|
|
||||||
|
Vectors.release(blockInWorld);
|
||||||
|
|
||||||
if (((TickableTile) tile).doesTickRegularly(tileTickContext)) {
|
if (((TickableTile) tile).doesTickRegularly(tileTickContext)) {
|
||||||
tickingTiles.add(new TileLocation(loc));
|
tickingTiles.add(new TileLocation(loc));
|
||||||
@ -109,8 +110,7 @@ public class ChunkLogic {
|
|||||||
tickingTiles.forEach(location -> {
|
tickingTiles.forEach(location -> {
|
||||||
action.accept(
|
action.accept(
|
||||||
location,
|
location,
|
||||||
getTilesOrNull(location.pos, location.face)
|
getTilesOrNull(location.pos, location.face).get(location.layer)
|
||||||
.get(location.layer)
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1,230 +0,0 @@
|
|||||||
package ru.windcorp.progressia.server.world;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
|
||||||
import ru.windcorp.progressia.common.comms.packets.Packet;
|
|
||||||
import ru.windcorp.progressia.common.comms.packets.PacketWorldChange;
|
|
||||||
import ru.windcorp.progressia.common.state.IOContext;
|
|
||||||
import ru.windcorp.progressia.common.util.LowOverheadCache;
|
|
||||||
import ru.windcorp.progressia.common.util.Vectors;
|
|
||||||
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
|
||||||
import ru.windcorp.progressia.common.world.Coordinates;
|
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
|
||||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
|
||||||
import ru.windcorp.progressia.common.world.entity.PacketEntityChange;
|
|
||||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
|
||||||
import ru.windcorp.progressia.server.Server;
|
|
||||||
|
|
||||||
public class ImplementedChangeTracker implements Changer {
|
|
||||||
|
|
||||||
public static interface ChangeImplementation {
|
|
||||||
void applyOnServer(WorldData world);
|
|
||||||
Packet asPacket();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SetBlock
|
|
||||||
extends PacketWorldChange
|
|
||||||
implements ChangeImplementation {
|
|
||||||
|
|
||||||
private final Vec3i position = new Vec3i();
|
|
||||||
private BlockData block;
|
|
||||||
|
|
||||||
public SetBlock() {
|
|
||||||
this("Core:SetBlock");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected SetBlock(String id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize(Vec3i position, BlockData block) {
|
|
||||||
this.position.set(position.x, position.y, position.z);
|
|
||||||
this.block = block;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyOnServer(WorldData world) {
|
|
||||||
Vec3i blockInChunk = Vectors.grab3i();
|
|
||||||
Coordinates.convertInWorldToInChunk(position, blockInChunk);
|
|
||||||
|
|
||||||
world.getChunkByBlock(position).setBlock(blockInChunk, block, true);
|
|
||||||
|
|
||||||
Vectors.release(blockInChunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void apply(WorldData world) {
|
|
||||||
applyOnServer(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Packet asPacket() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class AddOrRemoveTile
|
|
||||||
extends PacketWorldChange
|
|
||||||
implements ChangeImplementation {
|
|
||||||
|
|
||||||
private final Vec3i position = new Vec3i();
|
|
||||||
private BlockFace face;
|
|
||||||
private TileData tile;
|
|
||||||
|
|
||||||
private boolean shouldAdd;
|
|
||||||
|
|
||||||
public AddOrRemoveTile() {
|
|
||||||
this("Core:AddOrRemoveTile");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected AddOrRemoveTile(String id) {
|
|
||||||
super(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize(
|
|
||||||
Vec3i position, BlockFace face,
|
|
||||||
TileData tile,
|
|
||||||
boolean shouldAdd
|
|
||||||
) {
|
|
||||||
this.position.set(position.x, position.y, position.z);
|
|
||||||
this.face = face;
|
|
||||||
this.tile = tile;
|
|
||||||
this.shouldAdd = shouldAdd;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void applyOnServer(WorldData world) {
|
|
||||||
Vec3i blockInChunk = Vectors.grab3i();
|
|
||||||
Coordinates.convertInWorldToInChunk(position, blockInChunk);
|
|
||||||
|
|
||||||
List<TileData> tiles = world.getChunkByBlock(position).getTiles(blockInChunk, face);
|
|
||||||
|
|
||||||
if (shouldAdd) {
|
|
||||||
tiles.add(tile);
|
|
||||||
} else {
|
|
||||||
tiles.remove(tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vectors.release(blockInChunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void apply(WorldData world) {
|
|
||||||
applyOnServer(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Packet asPacket() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ChangeEntity implements ChangeImplementation {
|
|
||||||
|
|
||||||
private EntityData entity;
|
|
||||||
private Change<?> change;
|
|
||||||
|
|
||||||
private final PacketEntityChange packet = new PacketEntityChange();
|
|
||||||
|
|
||||||
public <T extends EntityData> void set(T entity, Change<T> change) {
|
|
||||||
this.entity = entity;
|
|
||||||
this.change = change;
|
|
||||||
|
|
||||||
packet.setEntityId(entity.getEntityId());
|
|
||||||
try {
|
|
||||||
entity.write(packet.getWriter(), IOContext.COMMS);
|
|
||||||
} catch (IOException e) {
|
|
||||||
CrashReports.report(e, "Could not write entity %s", entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Override
|
|
||||||
public void applyOnServer(WorldData world) {
|
|
||||||
((Change<EntityData>) change).change(entity);
|
|
||||||
|
|
||||||
try {
|
|
||||||
entity.write(packet.getWriter(), IOContext.COMMS);
|
|
||||||
} catch (IOException e) {
|
|
||||||
CrashReports.report(e, "Could not write entity %s", entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Packet asPacket() {
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private final List<ChangeImplementation> changes = new ArrayList<>(1024);
|
|
||||||
|
|
||||||
private final LowOverheadCache<SetBlock> setBlockCache =
|
|
||||||
new LowOverheadCache<>(SetBlock::new);
|
|
||||||
|
|
||||||
private final LowOverheadCache<AddOrRemoveTile> addOrRemoveTileCache =
|
|
||||||
new LowOverheadCache<>(AddOrRemoveTile::new);
|
|
||||||
|
|
||||||
private final LowOverheadCache<ChangeEntity> changeEntityCache =
|
|
||||||
new LowOverheadCache<>(ChangeEntity::new);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBlock(Vec3i pos, BlockData block) {
|
|
||||||
SetBlock change = setBlockCache.grab();
|
|
||||||
change.initialize(pos, block);
|
|
||||||
changes.add(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addTile(Vec3i block, BlockFace face, TileData tile) {
|
|
||||||
AddOrRemoveTile change = addOrRemoveTileCache.grab();
|
|
||||||
change.initialize(block, face, tile, true);
|
|
||||||
changes.add(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeTile(Vec3i block, BlockFace face, TileData tile) {
|
|
||||||
AddOrRemoveTile change = addOrRemoveTileCache.grab();
|
|
||||||
change.initialize(block, face, tile, false);
|
|
||||||
changes.add(change);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends EntityData> void changeEntity(
|
|
||||||
T entity, Change<T> change
|
|
||||||
) {
|
|
||||||
ChangeEntity changeRecord = changeEntityCache.grab();
|
|
||||||
changeRecord.set(entity, change);
|
|
||||||
changes.add(changeRecord);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void applyChanges(Server server) {
|
|
||||||
changes.forEach(c -> c.applyOnServer(server.getWorld().getData()));
|
|
||||||
changes.stream().map(ChangeImplementation::asPacket).filter(Objects::nonNull).forEach(
|
|
||||||
server.getClientManager()::broadcastGamePacket
|
|
||||||
);
|
|
||||||
changes.forEach(this::release);
|
|
||||||
changes.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void release(ChangeImplementation c) {
|
|
||||||
if (c instanceof SetBlock) {
|
|
||||||
setBlockCache.release((SetBlock) c);
|
|
||||||
} else if (c instanceof AddOrRemoveTile) {
|
|
||||||
addOrRemoveTileCache.release((AddOrRemoveTile) c);
|
|
||||||
} else if (c instanceof ChangeEntity) {
|
|
||||||
changeEntityCache.release((ChangeEntity) c);
|
|
||||||
} else {
|
|
||||||
throw new IllegalArgumentException("Could not find cache for " + c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -3,41 +3,42 @@ package ru.windcorp.progressia.server.world;
|
|||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.common.util.Vectors;
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
import ru.windcorp.progressia.common.world.Coordinates;
|
import ru.windcorp.progressia.common.world.Coordinates;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
import ru.windcorp.progressia.server.world.block.BlockTickContext;
|
import ru.windcorp.progressia.server.world.block.BlockTickContext;
|
||||||
|
|
||||||
public class MutableBlockTickContext
|
public class MutableBlockTickContext
|
||||||
extends MutableChunkTickContext
|
extends MutableTickContext
|
||||||
implements BlockTickContext {
|
implements BlockTickContext {
|
||||||
|
|
||||||
private final Vec3i blockInWorld = new Vec3i();
|
private final Vec3i blockInWorld = new Vec3i();
|
||||||
private final Vec3i blockInChunk = new Vec3i();
|
private ChunkLogic chunk;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vec3i getCoords() {
|
public Vec3i getBlockInWorld() {
|
||||||
return this.blockInWorld;
|
return this.blockInWorld;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setCoordsInWorld(Vec3i blockInWorld) {
|
||||||
public Vec3i getChunkCoords() {
|
getBlockInWorld().set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
|
||||||
return this.blockInChunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCoordsInWorld(Vec3i coords) {
|
|
||||||
getCoords().set(coords.x, coords.y, coords.z);
|
|
||||||
Coordinates.convertInWorldToInChunk(getCoords(), getChunkCoords());
|
|
||||||
|
|
||||||
Vec3i chunk = Vectors.grab3i();
|
Vec3i chunk = Vectors.grab3i();
|
||||||
Coordinates.convertInWorldToChunk(coords, chunk);
|
Coordinates.convertInWorldToChunk(blockInWorld, chunk);
|
||||||
setChunk(getWorld().getChunk(chunk));
|
setChunk(getWorld().getChunk(chunk));
|
||||||
Vectors.release(chunk);
|
Vectors.release(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCoordsInChunk(Vec3i coords) {
|
@Override
|
||||||
getChunkCoords().set(coords.x, coords.y, coords.z);
|
public ChunkLogic getChunk() {
|
||||||
Coordinates.getInWorld(
|
return chunk;
|
||||||
getChunkData().getPosition(), getChunkCoords(),
|
}
|
||||||
getCoords()
|
|
||||||
);
|
public void setChunk(ChunkLogic chunk) {
|
||||||
|
this.chunk = chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(Server server, Vec3i blockInWorld) {
|
||||||
|
setServer(server);
|
||||||
|
setCoordsInWorld(blockInWorld);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,66 +0,0 @@
|
|||||||
package ru.windcorp.progressia.server.world;
|
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
|
||||||
import ru.windcorp.progressia.server.Server;
|
|
||||||
|
|
||||||
public class MutableChunkTickContext implements ChunkTickContext {
|
|
||||||
|
|
||||||
private double tickLength;
|
|
||||||
private Server server;
|
|
||||||
private WorldLogic world;
|
|
||||||
private ChunkLogic chunk;
|
|
||||||
|
|
||||||
public MutableChunkTickContext() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getTickLength() {
|
|
||||||
return tickLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTickLength(double tickLength) {
|
|
||||||
this.tickLength = tickLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Server getServer() {
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setServer(Server server) {
|
|
||||||
this.server = server;
|
|
||||||
setWorld(server.getWorld());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WorldLogic getWorld() {
|
|
||||||
return world;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWorld(WorldLogic world) {
|
|
||||||
this.world = world;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChunkLogic getChunk() {
|
|
||||||
return chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChunk(ChunkLogic chunk) {
|
|
||||||
this.chunk = chunk;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void requestBlockTick(Vec3i blockInWorld) {
|
|
||||||
// TODO implement
|
|
||||||
throw new UnsupportedOperationException("Not yet implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void requestTileTick(Vec3i blockInWorld, BlockFace face, int layer) {
|
|
||||||
// TODO implement
|
|
||||||
throw new UnsupportedOperationException("Not yet implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,43 @@
|
|||||||
|
package ru.windcorp.progressia.server.world;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
|
||||||
|
public class MutableTickContext implements TickContext {
|
||||||
|
|
||||||
|
private double tickLength;
|
||||||
|
private Server server;
|
||||||
|
private WorldLogic world;
|
||||||
|
|
||||||
|
public MutableTickContext() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTickLength() {
|
||||||
|
return tickLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTickLength(double tickLength) {
|
||||||
|
this.tickLength = tickLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Server getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServer(Server server) {
|
||||||
|
this.server = server;
|
||||||
|
this.setTickLength(server.getTickLength());
|
||||||
|
setWorld(server.getWorld());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WorldLogic getWorld() {
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWorld(WorldLogic world) {
|
||||||
|
this.world = world;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,60 +1,47 @@
|
|||||||
package ru.windcorp.progressia.server.world;
|
package ru.windcorp.progressia.server.world;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.common.util.Vectors;
|
|
||||||
import ru.windcorp.progressia.common.world.Coordinates;
|
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
import ru.windcorp.progressia.server.world.tile.TileTickContext;
|
import ru.windcorp.progressia.server.world.tile.TileTickContext;
|
||||||
|
|
||||||
public class MutableTileTickContext
|
public class MutableTileTickContext
|
||||||
extends MutableChunkTickContext
|
extends MutableTickContext
|
||||||
implements TileTickContext {
|
implements TileTickContext {
|
||||||
|
|
||||||
private final Vec3i blockInWorld = new Vec3i();
|
private final Vec3i currentBlockInWorld = new Vec3i();
|
||||||
private final Vec3i blockInChunk = new Vec3i();
|
private final Vec3i counterBlockInWorld = new Vec3i();
|
||||||
|
|
||||||
private BlockFace face;
|
private BlockFace face;
|
||||||
private int layer;
|
private int layer;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vec3i getCoords() {
|
public Vec3i getCurrentBlockInWorld() {
|
||||||
return this.blockInWorld;
|
return this.currentBlockInWorld;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vec3i getChunkCoords() {
|
public Vec3i getCounterBlockInWorld() {
|
||||||
return this.blockInChunk;
|
return this.counterBlockInWorld;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCoordsInWorld(Vec3i coords) {
|
public void setCoordsInWorld(Vec3i currentBlockInWorld) {
|
||||||
getCoords().set(coords.x, coords.y, coords.z);
|
getCurrentBlockInWorld().set(currentBlockInWorld.x, currentBlockInWorld.y, currentBlockInWorld.z);
|
||||||
Coordinates.convertInWorldToInChunk(getCoords(), getChunkCoords());
|
getCounterBlockInWorld().set(currentBlockInWorld.x, currentBlockInWorld.y, currentBlockInWorld.z).add(getCurrentFace().getVector());
|
||||||
|
|
||||||
Vec3i chunk = Vectors.grab3i();
|
|
||||||
Coordinates.convertInWorldToChunk(coords, chunk);
|
|
||||||
setChunk(getWorld().getChunk(chunk));
|
|
||||||
Vectors.release(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCoordsInChunk(Vec3i coords) {
|
|
||||||
getChunkCoords().set(coords.x, coords.y, coords.z);
|
|
||||||
Coordinates.getInWorld(
|
|
||||||
getChunkData().getPosition(), getChunkCoords(),
|
|
||||||
getCoords()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlockFace getFace() {
|
public BlockFace getCurrentFace() {
|
||||||
return face;
|
return face;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFace(BlockFace face) {
|
public void setFace(BlockFace face) {
|
||||||
this.face = face;
|
this.face = face;
|
||||||
|
setCoordsInWorld(getCurrentBlockInWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getLayer() {
|
public int getCurrentLayer() {
|
||||||
return layer;
|
return layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,4 +49,11 @@ implements TileTickContext {
|
|||||||
this.layer = layer;
|
this.layer = layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void init(Server server, Vec3i blockInWorld, BlockFace face, int layer) {
|
||||||
|
setServer(server);
|
||||||
|
setFace(face);
|
||||||
|
setCoordsInWorld(blockInWorld);
|
||||||
|
setLayer(layer);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,163 @@
|
|||||||
|
package ru.windcorp.progressia.server.world;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.util.LowOverheadCache;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
||||||
|
import ru.windcorp.progressia.server.world.block.BlockTickContext;
|
||||||
|
import ru.windcorp.progressia.server.world.block.TickableBlock;
|
||||||
|
import ru.windcorp.progressia.server.world.block.UpdateableBlock;
|
||||||
|
import ru.windcorp.progressia.server.world.tile.TickableTile;
|
||||||
|
import ru.windcorp.progressia.server.world.tile.TileLogic;
|
||||||
|
import ru.windcorp.progressia.server.world.tile.TileTickContext;
|
||||||
|
import ru.windcorp.progressia.server.world.tile.UpdateableTile;
|
||||||
|
|
||||||
|
public class TickAndUpdateUtil {
|
||||||
|
|
||||||
|
private static final LowOverheadCache<MutableBlockTickContext> JAVAPONY_S_ULTIMATE_BLOCK_TICK_CONTEXT_SUPPLY =
|
||||||
|
new LowOverheadCache<>(MutableBlockTickContext::new);
|
||||||
|
|
||||||
|
private static final LowOverheadCache<MutableTileTickContext> JAVAPONY_S_ULTIMATE_TILE_TICK_CONTEXT_SUPPLY =
|
||||||
|
new LowOverheadCache<>(MutableTileTickContext::new);
|
||||||
|
|
||||||
|
public static void tickBlock(TickableBlock block, BlockTickContext context) {
|
||||||
|
try {
|
||||||
|
block.tick(context);
|
||||||
|
} catch (Exception e) {
|
||||||
|
CrashReports.report(e, "Could not tick block {}", block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void tickBlock(WorldLogic world, Vec3i blockInWorld) {
|
||||||
|
BlockLogic block = world.getBlock(blockInWorld);
|
||||||
|
if (!(block instanceof TickableBlock)) return; // also checks nulls
|
||||||
|
|
||||||
|
BlockTickContext tickContext = grabBlockTickContext(world.getServer(), blockInWorld);
|
||||||
|
tickBlock((TickableBlock) block, tickContext);
|
||||||
|
releaseTickContext(tickContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void tickTile(TickableTile tile, TileTickContext context) {
|
||||||
|
try {
|
||||||
|
tile.tick(context);
|
||||||
|
} catch (Exception e) {
|
||||||
|
CrashReports.report(e, "Could not tick tile {}", tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void tickTile(WorldLogic world, Vec3i blockInWorld, BlockFace face, int layer) {
|
||||||
|
TileLogic tile = world.getTile(blockInWorld, face, layer);
|
||||||
|
if (!(tile instanceof TickableTile)) return;
|
||||||
|
|
||||||
|
TileTickContext tickContext = grabTileTickContext(world.getServer(), blockInWorld, face, layer);
|
||||||
|
tickTile((TickableTile) tile, tickContext);
|
||||||
|
releaseTickContext(tickContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void tickTiles(WorldLogic world, Vec3i blockInWorld, BlockFace face) {
|
||||||
|
List<TileLogic> tiles = world.getTilesOrNull(blockInWorld, face);
|
||||||
|
if (tiles == null || tiles.isEmpty()) return;
|
||||||
|
|
||||||
|
MutableTileTickContext tickContext = JAVAPONY_S_ULTIMATE_TILE_TICK_CONTEXT_SUPPLY.grab();
|
||||||
|
|
||||||
|
for (int layer = 0; layer < tiles.size(); ++layer) {
|
||||||
|
TileLogic tile = tiles.get(layer);
|
||||||
|
if (!(tile instanceof TickableTile)) return;
|
||||||
|
|
||||||
|
tickContext.init(world.getServer(), blockInWorld, face, layer);
|
||||||
|
tickTile((TickableTile) tile, tickContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
JAVAPONY_S_ULTIMATE_TILE_TICK_CONTEXT_SUPPLY.release(tickContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateBlock(UpdateableBlock block, BlockTickContext context) {
|
||||||
|
try {
|
||||||
|
block.update(context);
|
||||||
|
} catch (Exception e) {
|
||||||
|
CrashReports.report(e, "Could not update block {}", block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateBlock(WorldLogic world, Vec3i blockInWorld) {
|
||||||
|
BlockLogic block = world.getBlock(blockInWorld);
|
||||||
|
if (!(block instanceof UpdateableBlock)) return; // also checks nulls
|
||||||
|
|
||||||
|
BlockTickContext tickContext = grabBlockTickContext(world.getServer(), blockInWorld);
|
||||||
|
updateBlock((UpdateableBlock) block, tickContext);
|
||||||
|
releaseTickContext(tickContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateTile(UpdateableTile tile, TileTickContext context) {
|
||||||
|
try {
|
||||||
|
tile.update(context);
|
||||||
|
} catch (Exception e) {
|
||||||
|
CrashReports.report(e, "Could not update tile {}", tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateTile(WorldLogic world, Vec3i blockInWorld, BlockFace face, int layer) {
|
||||||
|
TileLogic tile = world.getTile(blockInWorld, face, layer);
|
||||||
|
if (!(tile instanceof UpdateableTile)) return;
|
||||||
|
|
||||||
|
TileTickContext tickContext = grabTileTickContext(world.getServer(), blockInWorld, face, layer);
|
||||||
|
updateTile((UpdateableTile) tile, tickContext);
|
||||||
|
releaseTickContext(tickContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void updateTiles(WorldLogic world, Vec3i blockInWorld, BlockFace face) {
|
||||||
|
List<TileLogic> tiles = world.getTilesOrNull(blockInWorld, face);
|
||||||
|
if (tiles == null || tiles.isEmpty()) return;
|
||||||
|
|
||||||
|
MutableTileTickContext tickContext = JAVAPONY_S_ULTIMATE_TILE_TICK_CONTEXT_SUPPLY.grab();
|
||||||
|
|
||||||
|
for (int layer = 0; layer < tiles.size(); ++layer) {
|
||||||
|
TileLogic tile = tiles.get(layer);
|
||||||
|
if (!(tile instanceof UpdateableTile)) return;
|
||||||
|
|
||||||
|
tickContext.init(world.getServer(), blockInWorld, face, layer);
|
||||||
|
updateTile((UpdateableTile) tile, tickContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
JAVAPONY_S_ULTIMATE_TILE_TICK_CONTEXT_SUPPLY.release(tickContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockTickContext grabBlockTickContext(
|
||||||
|
Server server,
|
||||||
|
Vec3i blockInWorld
|
||||||
|
) {
|
||||||
|
MutableBlockTickContext result = JAVAPONY_S_ULTIMATE_BLOCK_TICK_CONTEXT_SUPPLY.grab();
|
||||||
|
result.init(server, blockInWorld);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TileTickContext grabTileTickContext(
|
||||||
|
Server server,
|
||||||
|
Vec3i blockInWorld,
|
||||||
|
BlockFace face,
|
||||||
|
int layer
|
||||||
|
) {
|
||||||
|
MutableTileTickContext result = JAVAPONY_S_ULTIMATE_TILE_TICK_CONTEXT_SUPPLY.grab();
|
||||||
|
result.init(server, blockInWorld, face, layer);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void releaseTickContext(BlockTickContext context) {
|
||||||
|
JAVAPONY_S_ULTIMATE_BLOCK_TICK_CONTEXT_SUPPLY.release(
|
||||||
|
(MutableBlockTickContext) context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void releaseTickContext(TileTickContext context) {
|
||||||
|
JAVAPONY_S_ULTIMATE_TILE_TICK_CONTEXT_SUPPLY.release(
|
||||||
|
(MutableTileTickContext) context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TickAndUpdateUtil() {}
|
||||||
|
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
package ru.windcorp.progressia.server.world;
|
package ru.windcorp.progressia.server.world;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
|
||||||
import ru.windcorp.progressia.server.Server;
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
|
||||||
|
|
||||||
public interface TickContext {
|
public interface TickContext {
|
||||||
|
|
||||||
@ -15,12 +14,12 @@ public interface TickContext {
|
|||||||
return getServer().getWorld();
|
return getServer().getWorld();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default WorldAccessor getAccessor() {
|
||||||
|
return getServer().getWorldAccessor();
|
||||||
|
}
|
||||||
|
|
||||||
default WorldData getWorldData() {
|
default WorldData getWorldData() {
|
||||||
return getWorld().getData();
|
return getWorld().getData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void requestBlockTick(Vec3i blockInWorld);
|
|
||||||
|
|
||||||
void requestTileTick(Vec3i blockInWorld, BlockFace face, int layer);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
package ru.windcorp.progressia.server.world;
|
|
||||||
|
|
||||||
import ru.windcorp.progressia.server.Server;
|
|
||||||
import ru.windcorp.progressia.server.world.block.TickableBlock;
|
|
||||||
import ru.windcorp.progressia.server.world.entity.EntityLogic;
|
|
||||||
import ru.windcorp.progressia.server.world.entity.EntityLogicRegistry;
|
|
||||||
import ru.windcorp.progressia.server.world.tile.TickableTile;
|
|
||||||
|
|
||||||
public class Ticker implements Runnable {
|
|
||||||
|
|
||||||
private final ImplementedChangeTracker tracker =
|
|
||||||
new ImplementedChangeTracker();
|
|
||||||
|
|
||||||
private final MutableBlockTickContext blockTickContext =
|
|
||||||
new MutableBlockTickContext();
|
|
||||||
|
|
||||||
private final MutableTileTickContext tileTickContext =
|
|
||||||
new MutableTileTickContext();
|
|
||||||
|
|
||||||
private final Server server;
|
|
||||||
|
|
||||||
public Ticker(Server server) {
|
|
||||||
this.server = server;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
this.blockTickContext.setTickLength(1000 / 20);
|
|
||||||
server.getWorld().getChunks().forEach(chunk -> {
|
|
||||||
tickChunk(chunk);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tickChunk(ChunkLogic chunk) {
|
|
||||||
MutableBlockTickContext blockContext = this.blockTickContext;
|
|
||||||
MutableTileTickContext tileContext = this.tileTickContext;
|
|
||||||
|
|
||||||
blockContext.setServer(server);
|
|
||||||
tileContext.setServer(server);
|
|
||||||
|
|
||||||
blockContext.setChunk(chunk);
|
|
||||||
tileContext.setChunk(chunk);
|
|
||||||
|
|
||||||
tickRegularTickers(chunk, blockContext, tileContext);
|
|
||||||
tickRandomBlocks(chunk, blockContext, tileContext);
|
|
||||||
|
|
||||||
tickEntities(chunk, blockContext);
|
|
||||||
|
|
||||||
flushChanges(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tickEntities(
|
|
||||||
ChunkLogic chunk,
|
|
||||||
MutableChunkTickContext tickContext
|
|
||||||
) {
|
|
||||||
// TODO this is ugly
|
|
||||||
|
|
||||||
chunk.getData().getEntities().forEach(entity -> {
|
|
||||||
EntityLogic logic = EntityLogicRegistry.getInstance().get(
|
|
||||||
entity.getId()
|
|
||||||
);
|
|
||||||
|
|
||||||
logic.tick(entity, tickContext, tracker);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tickRegularTickers(
|
|
||||||
ChunkLogic chunk,
|
|
||||||
MutableBlockTickContext blockContext,
|
|
||||||
MutableTileTickContext tileContext
|
|
||||||
) {
|
|
||||||
chunk.forEachTickingBlock((blockInChunk, block) -> {
|
|
||||||
blockContext.setCoordsInChunk(blockInChunk);
|
|
||||||
((TickableBlock) block).tick(blockContext, tracker);
|
|
||||||
});
|
|
||||||
|
|
||||||
chunk.forEachTickingTile((locInChunk, tile) -> {
|
|
||||||
tileContext.setCoordsInChunk(locInChunk.pos);
|
|
||||||
tileContext.setFace(locInChunk.face);
|
|
||||||
tileContext.setLayer(locInChunk.layer);
|
|
||||||
|
|
||||||
((TickableTile) tile).tick(tileContext, tracker);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void tickRandomBlocks(
|
|
||||||
ChunkLogic chunk,
|
|
||||||
MutableBlockTickContext blockContext,
|
|
||||||
MutableTileTickContext tileContext
|
|
||||||
) {
|
|
||||||
// TODO implement
|
|
||||||
}
|
|
||||||
|
|
||||||
private void flushChanges(ChunkLogic chunk) {
|
|
||||||
this.tracker.applyChanges(server);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -2,24 +2,34 @@ package ru.windcorp.progressia.server.world;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.common.util.Vectors;
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkDataListener;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkDataListeners;
|
||||||
import ru.windcorp.progressia.common.world.Coordinates;
|
import ru.windcorp.progressia.common.world.Coordinates;
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
import ru.windcorp.progressia.common.world.WorldDataListener;
|
import ru.windcorp.progressia.common.world.WorldDataListener;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
||||||
|
import ru.windcorp.progressia.server.world.tile.TileLogic;
|
||||||
|
|
||||||
public class WorldLogic {
|
public class WorldLogic {
|
||||||
|
|
||||||
private final WorldData data;
|
private final WorldData data;
|
||||||
|
private final Server server;
|
||||||
|
|
||||||
private final Map<ChunkData, ChunkLogic> chunks = new HashMap<>();
|
private final Map<ChunkData, ChunkLogic> chunks = new HashMap<>();
|
||||||
|
|
||||||
public WorldLogic(WorldData data) {
|
public WorldLogic(WorldData data, Server server) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
this.server = server;
|
||||||
|
|
||||||
data.addListener(new WorldDataListener() {
|
data.addListener(new WorldDataListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -32,6 +42,33 @@ public class WorldLogic {
|
|||||||
chunks.remove(chunk);
|
chunks.remove(chunk);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
data.addListener(ChunkDataListeners.createAdder(new ChunkDataListener() {
|
||||||
|
@Override
|
||||||
|
public void onChunkBlockChanged(
|
||||||
|
ChunkData chunk, Vec3i blockInChunk, BlockData previous, BlockData current
|
||||||
|
) {
|
||||||
|
Vec3i blockInWorld = Vectors.grab3i();
|
||||||
|
Coordinates.getInWorld(chunk.getPosition(), blockInChunk, blockInWorld);
|
||||||
|
getServer().getWorldAccessor().triggerUpdates(blockInWorld);
|
||||||
|
Vectors.release(blockInWorld);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChunkTilesChanged(
|
||||||
|
ChunkData chunk, Vec3i blockInChunk, BlockFace face, TileData tile,
|
||||||
|
boolean wasAdded
|
||||||
|
) {
|
||||||
|
Vec3i blockInWorld = Vectors.grab3i();
|
||||||
|
Coordinates.getInWorld(chunk.getPosition(), blockInChunk, blockInWorld);
|
||||||
|
getServer().getWorldAccessor().triggerUpdates(blockInWorld, face);
|
||||||
|
Vectors.release(blockInWorld);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Server getServer() {
|
||||||
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WorldData getData() {
|
public WorldData getData() {
|
||||||
@ -61,10 +98,42 @@ public class WorldLogic {
|
|||||||
Vec3i blockInChunk = Vectors.grab3i();
|
Vec3i blockInChunk = Vectors.grab3i();
|
||||||
Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk);
|
Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk);
|
||||||
BlockLogic result = chunk.getBlock(blockInChunk);
|
BlockLogic result = chunk.getBlock(blockInChunk);
|
||||||
|
Vectors.release(blockInChunk);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TileLogic> getTiles(Vec3i blockInWorld, BlockFace face) {
|
||||||
|
return getTilesImpl(blockInWorld, face, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TileLogic> getTilesOrNull(Vec3i blockInWorld, BlockFace face) {
|
||||||
|
return getTilesImpl(blockInWorld, face, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TileLogic> getTilesImpl(Vec3i blockInWorld, BlockFace face, boolean createIfMissing) {
|
||||||
|
ChunkLogic chunk = getChunkByBlock(blockInWorld);
|
||||||
|
if (chunk == null) return null;
|
||||||
|
|
||||||
|
Vec3i blockInChunk = Vectors.grab3i();
|
||||||
|
Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk);
|
||||||
|
|
||||||
|
List<TileLogic> result =
|
||||||
|
createIfMissing
|
||||||
|
? chunk.getTiles(blockInChunk, face)
|
||||||
|
: chunk.getTilesOrNull(blockInChunk, face);
|
||||||
|
|
||||||
|
Vectors.release(blockInChunk);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TileLogic getTile(Vec3i blockInWorld, BlockFace face, int layer) {
|
||||||
|
List<TileLogic> tiles = getTilesOrNull(blockInWorld, face);
|
||||||
|
if (tiles == null || tiles.size() <= layer) return null;
|
||||||
|
|
||||||
|
return tiles.get(layer);
|
||||||
|
}
|
||||||
|
|
||||||
public Collection<ChunkLogic> getChunks() {
|
public Collection<ChunkLogic> getChunks() {
|
||||||
return chunks.values();
|
return chunks.values();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package ru.windcorp.progressia.server.world.block;
|
package ru.windcorp.progressia.server.world.block;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
|
||||||
public class BlockLogic extends Namespaced {
|
public class BlockLogic extends Namespaced {
|
||||||
|
|
||||||
@ -8,4 +9,20 @@ public class BlockLogic extends Namespaced {
|
|||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isSolid(BlockTickContext context, BlockFace face) {
|
||||||
|
return isSolid(face);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSolid(BlockFace face) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTransparent(BlockTickContext context) {
|
||||||
|
return isTransparent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTransparent() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -10,20 +10,14 @@ public interface BlockTickContext extends ChunkTickContext {
|
|||||||
* Returns the current world coordinates.
|
* Returns the current world coordinates.
|
||||||
* @return the world coordinates of the block being ticked
|
* @return the world coordinates of the block being ticked
|
||||||
*/
|
*/
|
||||||
Vec3i getCoords();
|
Vec3i getBlockInWorld();
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current chunk coordinates.
|
|
||||||
* @return the chunk coordinates of the block being ticked
|
|
||||||
*/
|
|
||||||
Vec3i getChunkCoords();
|
|
||||||
|
|
||||||
default BlockLogic getBlock() {
|
default BlockLogic getBlock() {
|
||||||
return getChunk().getBlock(getChunkCoords());
|
return getWorld().getBlock(getBlockInWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
default BlockData getBlockData() {
|
default BlockData getBlockData() {
|
||||||
return getChunkData().getBlock(getChunkCoords());
|
return getWorldData().getBlock(getBlockInWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
package ru.windcorp.progressia.server.world.block;
|
package ru.windcorp.progressia.server.world.block;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
|
||||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
|
||||||
import ru.windcorp.progressia.server.Server;
|
import ru.windcorp.progressia.server.Server;
|
||||||
import ru.windcorp.progressia.server.world.ChunkLogic;
|
import ru.windcorp.progressia.server.world.ChunkLogic;
|
||||||
import ru.windcorp.progressia.server.world.WorldLogic;
|
|
||||||
|
|
||||||
public class ForwardingBlockTickContext implements BlockTickContext {
|
public class ForwardingBlockTickContext implements BlockTickContext {
|
||||||
|
|
||||||
@ -30,11 +25,6 @@ public class ForwardingBlockTickContext implements BlockTickContext {
|
|||||||
return parent.getChunk();
|
return parent.getChunk();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChunkData getChunkData() {
|
|
||||||
return parent.getChunkData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getTickLength() {
|
public double getTickLength() {
|
||||||
return parent.getTickLength();
|
return parent.getTickLength();
|
||||||
@ -46,43 +36,8 @@ public class ForwardingBlockTickContext implements BlockTickContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vec3i getCoords() {
|
public Vec3i getBlockInWorld() {
|
||||||
return parent.getCoords();
|
return parent.getBlockInWorld();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WorldLogic getWorld() {
|
|
||||||
return parent.getWorld();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public WorldData getWorldData() {
|
|
||||||
return parent.getWorldData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vec3i getChunkCoords() {
|
|
||||||
return parent.getChunkCoords();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void requestBlockTick(Vec3i blockInWorld) {
|
|
||||||
parent.requestBlockTick(blockInWorld);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void requestTileTick(Vec3i blockInWorld, BlockFace face, int layer) {
|
|
||||||
parent.requestTileTick(blockInWorld, face, layer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockLogic getBlock() {
|
|
||||||
return parent.getBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockData getBlockData() {
|
|
||||||
return parent.getBlockData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
package ru.windcorp.progressia.server.world.block;
|
package ru.windcorp.progressia.server.world.block;
|
||||||
|
|
||||||
import ru.windcorp.progressia.server.world.Changer;
|
|
||||||
|
|
||||||
public interface TickableBlock {
|
public interface TickableBlock {
|
||||||
|
|
||||||
void tick(BlockTickContext context, Changer changer);
|
void tick(BlockTickContext context);
|
||||||
|
|
||||||
default boolean doesTickRegularly(BlockTickContext context) {
|
default boolean doesTickRegularly(BlockTickContext context) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package ru.windcorp.progressia.server.world.block;
|
package ru.windcorp.progressia.server.world.block;
|
||||||
|
|
||||||
import ru.windcorp.progressia.server.world.Changer;
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
|
||||||
public interface UpdateableBlock {
|
public interface UpdateableBlock {
|
||||||
|
|
||||||
void update(BlockTickContext context, Changer changer);
|
default void update(BlockTickContext context) {
|
||||||
|
LogManager.getLogger().info("Updating block {} @ ({}; {}; {})", context.getBlock(), context.getBlockInWorld().x, context.getBlockInWorld().y, context.getBlockInWorld().z);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package ru.windcorp.progressia.server.world.entity;
|
|||||||
|
|
||||||
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
import ru.windcorp.progressia.common.util.namespaces.Namespaced;
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
import ru.windcorp.progressia.server.world.Changer;
|
|
||||||
import ru.windcorp.progressia.server.world.TickContext;
|
import ru.windcorp.progressia.server.world.TickContext;
|
||||||
|
|
||||||
public class EntityLogic extends Namespaced {
|
public class EntityLogic extends Namespaced {
|
||||||
@ -11,7 +10,7 @@ public class EntityLogic extends Namespaced {
|
|||||||
super(id);
|
super(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tick(EntityData entity, TickContext context, Changer changer) {
|
public void tick(EntityData entity, TickContext context) {
|
||||||
entity.incrementAge(context.getTickLength());
|
entity.incrementAge(context.getTickLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tasks;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.Coordinates;
|
||||||
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
|
|
||||||
|
class AddOrRemoveTile extends CachedWorldChange {
|
||||||
|
|
||||||
|
private final Vec3i blockInWorld = new Vec3i();
|
||||||
|
private BlockFace face;
|
||||||
|
private TileData tile;
|
||||||
|
|
||||||
|
private boolean shouldAdd;
|
||||||
|
|
||||||
|
public AddOrRemoveTile(Consumer<? super CachedChange> disposer) {
|
||||||
|
super(disposer, "Core:AddOrRemoveTile");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(
|
||||||
|
Vec3i position, BlockFace face,
|
||||||
|
TileData tile,
|
||||||
|
boolean shouldAdd
|
||||||
|
) {
|
||||||
|
if (this.tile != null)
|
||||||
|
throw new IllegalStateException("Payload is not null. Current: " + this.tile + "; requested: " + tile);
|
||||||
|
|
||||||
|
this.blockInWorld.set(position.x, position.y, position.z);
|
||||||
|
this.face = face;
|
||||||
|
this.tile = tile;
|
||||||
|
this.shouldAdd = shouldAdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void affectCommon(WorldData world) {
|
||||||
|
Vec3i blockInChunk = Vectors.grab3i();
|
||||||
|
Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk);
|
||||||
|
|
||||||
|
List<TileData> tiles = world.getChunkByBlock(blockInWorld).getTiles(blockInChunk, face);
|
||||||
|
|
||||||
|
if (shouldAdd) {
|
||||||
|
tiles.add(tile);
|
||||||
|
} else {
|
||||||
|
tiles.remove(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vectors.release(blockInChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getRelevantChunk(Vec3i output) {
|
||||||
|
Coordinates.convertInWorldToChunk(blockInWorld, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
this.tile = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tasks;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.Coordinates;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
import ru.windcorp.progressia.server.world.TickAndUpdateUtil;
|
||||||
|
import ru.windcorp.progressia.server.world.WorldLogic;
|
||||||
|
|
||||||
|
class BlockTriggeredUpdate extends CachedEvaluation {
|
||||||
|
|
||||||
|
private final Vec3i blockInWorld = new Vec3i();
|
||||||
|
|
||||||
|
public BlockTriggeredUpdate(Consumer<? super CachedEvaluation> disposer) {
|
||||||
|
super(disposer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evaluate(Server server) {
|
||||||
|
Vec3i cursor = Vectors.grab3i();
|
||||||
|
cursor.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
|
||||||
|
|
||||||
|
WorldLogic world = server.getWorld();
|
||||||
|
|
||||||
|
for (BlockFace face : BlockFace.getFaces()) {
|
||||||
|
TickAndUpdateUtil.updateTiles(world, cursor, face);
|
||||||
|
cursor.add(face.getVector());
|
||||||
|
TickAndUpdateUtil.updateBlock(world, cursor);
|
||||||
|
cursor.sub(face.getVector());
|
||||||
|
}
|
||||||
|
|
||||||
|
Vectors.release(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(Vec3i blockInWorld) {
|
||||||
|
this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getRelevantChunk(Vec3i output) {
|
||||||
|
Coordinates.convertInWorldToChunk(blockInWorld, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tasks;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.server.world.ticking.Change;
|
||||||
|
|
||||||
|
public abstract class CachedChange extends Change {
|
||||||
|
|
||||||
|
private final Consumer<? super CachedChange> disposer;
|
||||||
|
|
||||||
|
public CachedChange(Consumer<? super CachedChange> disposer) {
|
||||||
|
this.disposer = disposer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
disposer.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tasks;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.server.world.ticking.Evaluation;
|
||||||
|
|
||||||
|
public abstract class CachedEvaluation extends Evaluation {
|
||||||
|
|
||||||
|
private final Consumer<? super CachedEvaluation> disposer;
|
||||||
|
|
||||||
|
public CachedEvaluation(Consumer<? super CachedEvaluation> disposer) {
|
||||||
|
this.disposer = disposer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
disposer.accept(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,36 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tasks;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.comms.packets.PacketWorldChange;
|
||||||
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
|
||||||
|
public abstract class CachedWorldChange extends CachedChange {
|
||||||
|
|
||||||
|
private final PacketWorldChange packet;
|
||||||
|
|
||||||
|
public CachedWorldChange(Consumer<? super CachedChange> disposer, String packetId) {
|
||||||
|
super(disposer);
|
||||||
|
|
||||||
|
this.packet = new PacketWorldChange(packetId) {
|
||||||
|
@Override
|
||||||
|
public void apply(WorldData world) {
|
||||||
|
affectCommon(world);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void affect(Server server) {
|
||||||
|
affectCommon(server.getWorld().getData());
|
||||||
|
server.getClientManager().broadcastGamePacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked by both Change and Packet.
|
||||||
|
* @param world the world to affect
|
||||||
|
*/
|
||||||
|
protected abstract void affectCommon(WorldData world);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tasks;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.state.IOContext;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
import ru.windcorp.progressia.common.world.entity.PacketEntityChange;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
|
||||||
|
class ChangeEntity extends CachedChange {
|
||||||
|
|
||||||
|
private EntityData entity;
|
||||||
|
private StateChange<?> change;
|
||||||
|
|
||||||
|
private final PacketEntityChange packet = new PacketEntityChange();
|
||||||
|
|
||||||
|
public ChangeEntity(Consumer<? super CachedChange> disposer) {
|
||||||
|
super(disposer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EntityData> void set(T entity, StateChange<T> change) {
|
||||||
|
if (this.entity != null)
|
||||||
|
throw new IllegalStateException("Entity is not null. Current: " + this.entity + "; requested: " + entity);
|
||||||
|
|
||||||
|
if (this.change != null)
|
||||||
|
throw new IllegalStateException("Change is not null. Current: " + this.change + "; requested: " + change);
|
||||||
|
|
||||||
|
this.entity = entity;
|
||||||
|
this.change = change;
|
||||||
|
|
||||||
|
packet.setEntityId(entity.getEntityId());
|
||||||
|
try {
|
||||||
|
entity.write(packet.getWriter(), IOContext.COMMS); // TODO wtf is this... (see whole file)
|
||||||
|
} catch (IOException e) {
|
||||||
|
CrashReports.report(e, "Could not write entity %s", entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void affect(Server server) {
|
||||||
|
((StateChange<EntityData>) change).change(entity);
|
||||||
|
|
||||||
|
try {
|
||||||
|
entity.write(packet.getWriter(), IOContext.COMMS); // ...and this doing at the same time? - javapony at 1 AM
|
||||||
|
} catch (IOException e) {
|
||||||
|
CrashReports.report(e, "Could not write entity %s", entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
server.getClientManager().broadcastGamePacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getRelevantChunk(Vec3i output) {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isThreadSensitive() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
this.entity = null;
|
||||||
|
this.change = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tasks;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.Coordinates;
|
||||||
|
import ru.windcorp.progressia.common.world.WorldData;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
|
|
||||||
|
class SetBlock extends CachedWorldChange {
|
||||||
|
|
||||||
|
private final Vec3i blockInWorld = new Vec3i();
|
||||||
|
private BlockData block;
|
||||||
|
|
||||||
|
public SetBlock(Consumer<? super CachedChange> disposer) {
|
||||||
|
super(disposer, "Core:SetBlock");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initialize(Vec3i blockInWorld, BlockData block) {
|
||||||
|
if (this.block != null)
|
||||||
|
throw new IllegalStateException("Payload is not null. Current: " + this.block + "; requested: " + block);
|
||||||
|
|
||||||
|
this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
|
||||||
|
this.block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void affectCommon(WorldData world) {
|
||||||
|
Vec3i blockInChunk = Vectors.grab3i();
|
||||||
|
Coordinates.convertInWorldToInChunk(blockInWorld, blockInChunk);
|
||||||
|
|
||||||
|
world.getChunkByBlock(blockInWorld).setBlock(blockInChunk, block, true);
|
||||||
|
|
||||||
|
Vectors.release(blockInChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getRelevantChunk(Vec3i output) {
|
||||||
|
Coordinates.convertInWorldToChunk(blockInWorld, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
this.block = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tasks;
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface StateChange<T> {
|
||||||
|
void change(T object);
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tasks;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.Coordinates;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
import ru.windcorp.progressia.server.world.TickAndUpdateUtil;
|
||||||
|
import ru.windcorp.progressia.server.world.WorldLogic;
|
||||||
|
|
||||||
|
class TileTriggeredUpdate extends CachedEvaluation {
|
||||||
|
|
||||||
|
private final Vec3i blockInWorld = new Vec3i();
|
||||||
|
private BlockFace face = null;
|
||||||
|
|
||||||
|
public TileTriggeredUpdate(Consumer<? super CachedEvaluation> disposer) {
|
||||||
|
super(disposer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void evaluate(Server server) {
|
||||||
|
Vec3i cursor = Vectors.grab3i();
|
||||||
|
cursor.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
|
||||||
|
|
||||||
|
WorldLogic world = server.getWorld();
|
||||||
|
|
||||||
|
TickAndUpdateUtil.tickTiles(world, cursor, face); // Tick facemates (also self)
|
||||||
|
TickAndUpdateUtil.tickBlock(world, cursor); // Tick block on one side
|
||||||
|
cursor.add(face.getVector());
|
||||||
|
TickAndUpdateUtil.tickBlock(world, cursor); // Tick block on the other side
|
||||||
|
|
||||||
|
Vectors.release(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(Vec3i blockInWorld, BlockFace face) {
|
||||||
|
this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
|
||||||
|
this.face = face;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getRelevantChunk(Vec3i output) {
|
||||||
|
Coordinates.convertInWorldToChunk(blockInWorld, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,89 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tasks;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.util.MultiLOC;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
import ru.windcorp.progressia.server.world.ticking.TickerTask;
|
||||||
|
|
||||||
|
public class WorldAccessor {
|
||||||
|
|
||||||
|
private final MultiLOC cache;
|
||||||
|
{
|
||||||
|
MultiLOC mloc = new MultiLOC();
|
||||||
|
Consumer<TickerTask> disposer = mloc::release;
|
||||||
|
|
||||||
|
cache = mloc
|
||||||
|
.addClass(SetBlock.class, () -> new SetBlock(disposer))
|
||||||
|
.addClass(AddOrRemoveTile.class, () -> new AddOrRemoveTile(disposer))
|
||||||
|
.addClass(ChangeEntity.class, () -> new ChangeEntity(disposer))
|
||||||
|
|
||||||
|
.addClass(BlockTriggeredUpdate.class, () -> new BlockTriggeredUpdate(disposer))
|
||||||
|
.addClass(TileTriggeredUpdate.class, () -> new TileTriggeredUpdate(disposer));
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Server server;
|
||||||
|
|
||||||
|
public WorldAccessor(Server server) {
|
||||||
|
this.server = server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlock(Vec3i blockInWorld, BlockData block) {
|
||||||
|
SetBlock change = cache.grab(SetBlock.class);
|
||||||
|
change.initialize(blockInWorld, block);
|
||||||
|
server.requestChange(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTile(Vec3i blockInWorld, BlockFace face, TileData tile) {
|
||||||
|
AddOrRemoveTile change = cache.grab(AddOrRemoveTile.class);
|
||||||
|
change.initialize(blockInWorld, face, tile, true);
|
||||||
|
server.requestChange(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTile(Vec3i blockInWorld, BlockFace face, TileData tile) {
|
||||||
|
AddOrRemoveTile change = cache.grab(AddOrRemoveTile.class);
|
||||||
|
change.initialize(blockInWorld, face, tile, false);
|
||||||
|
server.requestChange(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T extends EntityData> void changeEntity(
|
||||||
|
T entity, StateChange<T> stateChange
|
||||||
|
) {
|
||||||
|
ChangeEntity change = cache.grab(ChangeEntity.class);
|
||||||
|
change.set(entity, stateChange);
|
||||||
|
server.requestChange(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tickBlock(Vec3i blockInWorld) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a block is the trigger
|
||||||
|
* @param blockInWorld
|
||||||
|
*/
|
||||||
|
// TODO rename to something meaningful
|
||||||
|
public void triggerUpdates(Vec3i blockInWorld) {
|
||||||
|
BlockTriggeredUpdate evaluation = cache.grab(BlockTriggeredUpdate.class);
|
||||||
|
evaluation.init(blockInWorld);
|
||||||
|
server.requestEvaluation(evaluation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a tile is the trigger
|
||||||
|
* @param blockInWorld
|
||||||
|
* @param face
|
||||||
|
*/
|
||||||
|
// TODO rename to something meaningful
|
||||||
|
public void triggerUpdates(Vec3i blockInWorld, BlockFace face) {
|
||||||
|
TileTriggeredUpdate evaluation = cache.grab(TileTriggeredUpdate.class);
|
||||||
|
evaluation.init(blockInWorld, face);
|
||||||
|
server.requestEvaluation(evaluation);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.ticking;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link TickerTask} that aims to perform a predetermined set of changes on the world.
|
||||||
|
* @author javapony
|
||||||
|
*/
|
||||||
|
public abstract class Change extends TickerTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the changes on the provided server instance.
|
||||||
|
* <p>
|
||||||
|
* This method will be executed when the world is in an inconsistent state and may not be queried,
|
||||||
|
* only changed. Therefore, all necessary inspection must be performed before this method is invoked,
|
||||||
|
* typically by an {@link Evaluation}. Failure to abide by this contract may lead to race conditions
|
||||||
|
* and/or devil invasions.
|
||||||
|
* @param server the {@link Server} instance to affect
|
||||||
|
*/
|
||||||
|
public abstract void affect(Server server);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void run(Server server) {
|
||||||
|
affect(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.ticking;
|
||||||
|
|
||||||
|
public class DevilInvasionException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = "devil666satan".hashCode();
|
||||||
|
|
||||||
|
private DevilInvasionException() {
|
||||||
|
// You don't choose when an invasion occurs.
|
||||||
|
// _They_ do.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.ticking;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link TickerTask} that needs to access the world for analysis.
|
||||||
|
* @author javapony
|
||||||
|
*/
|
||||||
|
public abstract class Evaluation extends TickerTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs the analysis of the provided server instance.
|
||||||
|
* <p>
|
||||||
|
* This method will be executed when the world is in an consistent state
|
||||||
|
* and may be queried for meaningful information. However, other
|
||||||
|
* evaluations may be happening concurrently, so any world modification
|
||||||
|
* is prohibited. Evaluations are expected to request {@link Change}s
|
||||||
|
* to interact with the world. Failure to abide by this contract may
|
||||||
|
* lead to race conditions and/or devil invasions.
|
||||||
|
* @param server the server instance to inspect
|
||||||
|
*/
|
||||||
|
public abstract void evaluate(Server server);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void run(Server server) {
|
||||||
|
evaluate(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,164 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.ticking;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
|
||||||
|
class Ticker {
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
private final int id;
|
||||||
|
|
||||||
|
private Thread thread = null;
|
||||||
|
private final TickerCoordinator coordinator;
|
||||||
|
|
||||||
|
private volatile boolean shouldRun = true;
|
||||||
|
|
||||||
|
// Expected to implement RandomAccess
|
||||||
|
private final List<TickerTask> tasks = new ArrayList<>(TickerCoordinator.INITIAL_QUEUE_SIZE);
|
||||||
|
|
||||||
|
private final Logger logger;
|
||||||
|
|
||||||
|
public Ticker(String name, int id, TickerCoordinator coordinator) {
|
||||||
|
this.name = Objects.requireNonNull(name, "name");
|
||||||
|
this.id = id;
|
||||||
|
this.coordinator = Objects.requireNonNull(coordinator, "coordinator");
|
||||||
|
|
||||||
|
this.logger = LogManager.getLogger(this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void start() {
|
||||||
|
if (thread != null)
|
||||||
|
throw new IllegalStateException("Ticker already started in thread " + thread);
|
||||||
|
|
||||||
|
thread = new Thread(this::run, this.name);
|
||||||
|
logger.debug("Starting");
|
||||||
|
thread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Thread getThread() {
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TickerCoordinator getCoordinator() {
|
||||||
|
return coordinator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void stop() {
|
||||||
|
if (thread == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
shouldRun = false;
|
||||||
|
thread.interrupt();
|
||||||
|
|
||||||
|
logger.debug("Stopping");
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void requestWork(Collection<TickerTask> tasks) {
|
||||||
|
int currentTaskCount = this.tasks.size();
|
||||||
|
if (currentTaskCount != 0) {
|
||||||
|
throw new IllegalStateException("Ticker already has " + currentTaskCount + " tasks");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tasks.addAll(Objects.requireNonNull(tasks, "tasks"));
|
||||||
|
this.notifyAll();
|
||||||
|
|
||||||
|
logger.debug("Work {} requested", tasks.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void run() {
|
||||||
|
try {
|
||||||
|
logger.debug("Started");
|
||||||
|
|
||||||
|
while (!Thread.interrupted()) {
|
||||||
|
boolean shouldStop = sleep();
|
||||||
|
if (shouldStop) break;
|
||||||
|
work();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Stopped");
|
||||||
|
|
||||||
|
// Do not release Thread reference so start() still throws ISE
|
||||||
|
} catch (Exception e) {
|
||||||
|
getCoordinator().crash(e, this.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized boolean sleep() {
|
||||||
|
logger.debug("Entering sleep");
|
||||||
|
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
|
||||||
|
if (!shouldRun) {
|
||||||
|
logger.debug("Exiting sleep: received stop request");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int taskCount = tasks.size();
|
||||||
|
if (taskCount > 0) {
|
||||||
|
logger.debug("Exiting sleep: received {} tasks", taskCount);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Waiting");
|
||||||
|
this.wait();
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.debug("Exiting sleep: interrupted");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void work() {
|
||||||
|
logger.debug("Starting work");
|
||||||
|
|
||||||
|
int tasksCompleted = runTasks();
|
||||||
|
resetState();
|
||||||
|
|
||||||
|
logger.debug("Work complete; run {} tasks", tasksCompleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int runTasks() {
|
||||||
|
int tasksCompleted = 0;
|
||||||
|
|
||||||
|
Server srv = getCoordinator().getServer();
|
||||||
|
|
||||||
|
for (int i = 0; i < tasks.size(); ++i) {
|
||||||
|
TickerTask task = tasks.get(i);
|
||||||
|
|
||||||
|
assert task != null : "Encountered null task";
|
||||||
|
|
||||||
|
try {
|
||||||
|
task.run(srv);
|
||||||
|
} catch (Exception e) {
|
||||||
|
CrashReports.report(e, "Could not run {} task {}", task.getClass().getSimpleName(), task);
|
||||||
|
}
|
||||||
|
|
||||||
|
tasksCompleted++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasksCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void resetState() {
|
||||||
|
tasks.clear();
|
||||||
|
getCoordinator().reportWorkComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,252 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.ticking;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.ConcurrentModificationException;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import com.google.common.collect.Collections2;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.Units;
|
||||||
|
import ru.windcorp.progressia.common.util.crash.CrashReports;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Central control point for serverside ticking. This class provides an interface to
|
||||||
|
* interact with Tickers.
|
||||||
|
* @author javapony
|
||||||
|
*/
|
||||||
|
public class TickerCoordinator {
|
||||||
|
|
||||||
|
static final int INITIAL_QUEUE_SIZE = 1024;
|
||||||
|
|
||||||
|
private final Server server;
|
||||||
|
|
||||||
|
// Synchronized manually
|
||||||
|
private final Collection<Change> pendingChanges = new ArrayList<>(INITIAL_QUEUE_SIZE);
|
||||||
|
// Synchronized manually
|
||||||
|
private final Collection<Evaluation> pendingEvaluations = new ArrayList<>(INITIAL_QUEUE_SIZE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cached ArrayList used to transfer tasks from Coordinator to Tickers.
|
||||||
|
* This list must be empty when not in {@link #startPassStage(Collection, String)}.
|
||||||
|
*/
|
||||||
|
private final Collection<TickerTask> cachedTransferList = new ArrayList<>(INITIAL_QUEUE_SIZE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All tasks that must be {@linkplain TickerTask#dispose() disposed of} at the end of the current
|
||||||
|
* tick. This list must be empty when not in {@link #runPassStage(Collection, String)}.
|
||||||
|
*/
|
||||||
|
private final Collection<TickerTask> toDispose = new ArrayList<>(INITIAL_QUEUE_SIZE);
|
||||||
|
|
||||||
|
private final Collection<Ticker> tickers;
|
||||||
|
private final Collection<Thread> threads;
|
||||||
|
|
||||||
|
private final AtomicInteger workingTickers = new AtomicInteger();
|
||||||
|
|
||||||
|
private final Logger logger = LogManager.getLogger("Ticker Coordinator");
|
||||||
|
|
||||||
|
public TickerCoordinator(Server server, int tickers) {
|
||||||
|
this.server = Objects.requireNonNull(server, "server");
|
||||||
|
|
||||||
|
Collection<Ticker> tickerCollection = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 0; i < tickers; ++i) {
|
||||||
|
tickerCollection.add(new Ticker("Ticker " + i, i, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.tickers = ImmutableList.copyOf(tickerCollection);
|
||||||
|
this.threads = Collections2.transform(this.tickers, Ticker::getThread); // Immutable because it is a view
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Public API
|
||||||
|
*/
|
||||||
|
|
||||||
|
public synchronized void start() {
|
||||||
|
logger.debug("Starting tickers");
|
||||||
|
tickers.forEach(Ticker::start);
|
||||||
|
logger.debug("Tickers started");
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void stop() {
|
||||||
|
logger.debug("Stopping tickers");
|
||||||
|
tickers.forEach(Ticker::stop);
|
||||||
|
logger.debug("Tickers requested to stop");
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void requestChange(Change change) {
|
||||||
|
pendingChanges.add(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void requestEvaluation(Evaluation evaluation) {
|
||||||
|
pendingEvaluations.add(evaluation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Server getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<Thread> getThreads() {
|
||||||
|
return this.threads;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTickLength() {
|
||||||
|
// TODO implement
|
||||||
|
return Units.SECONDS / 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* runOneTick & Friends
|
||||||
|
*/
|
||||||
|
|
||||||
|
public void runOneTick() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
int passes = 0;
|
||||||
|
|
||||||
|
logger.debug("Beginning tick");
|
||||||
|
|
||||||
|
while (hasPending()) {
|
||||||
|
logger.debug("Starting pass");
|
||||||
|
runOnePass();
|
||||||
|
logger.debug("Pass complete");
|
||||||
|
passes++;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Tick complete; run {} passes", passes);
|
||||||
|
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// Exit silently
|
||||||
|
|
||||||
|
// ...or almost silently
|
||||||
|
logger.debug("Tick interrupted. WTF?");
|
||||||
|
} catch (Exception e) {
|
||||||
|
crash(e, "Coordinator");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasPending() {
|
||||||
|
// Race condition?
|
||||||
|
return !(pendingChanges.isEmpty() && pendingEvaluations.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void runOnePass() throws InterruptedException {
|
||||||
|
runPassStage(pendingEvaluations, "EVALUATION");
|
||||||
|
runPassStage(pendingChanges, "CHANGE");
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void runPassStage(
|
||||||
|
Collection<? extends TickerTask> tasks,
|
||||||
|
String stageName
|
||||||
|
) throws InterruptedException {
|
||||||
|
if (!toDispose.isEmpty())
|
||||||
|
throw new IllegalStateException("toDispose is not empty: " + toDispose);
|
||||||
|
|
||||||
|
Collection<TickerTask> toDispose = this.toDispose;
|
||||||
|
|
||||||
|
startPassStage(tasks, toDispose, stageName);
|
||||||
|
sync();
|
||||||
|
dispose(toDispose);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dispose(Collection<TickerTask> toDispose) {
|
||||||
|
toDispose.forEach(TickerTask::dispose);
|
||||||
|
toDispose.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void startPassStage(
|
||||||
|
Collection<? extends TickerTask> tasks,
|
||||||
|
Collection<TickerTask> toDispose,
|
||||||
|
String stageName
|
||||||
|
) {
|
||||||
|
if (tasks.isEmpty()) {
|
||||||
|
logger.debug("Skipping stage {}: tasks is empty", stageName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("Starting stage {}", stageName);
|
||||||
|
|
||||||
|
if (!cachedTransferList.isEmpty())
|
||||||
|
throw new IllegalStateException("cachedTransferList is not empty: " + cachedTransferList);
|
||||||
|
|
||||||
|
workingTickers.set(0);
|
||||||
|
|
||||||
|
for (Ticker ticker : tickers) {
|
||||||
|
workingTickers.incrementAndGet();
|
||||||
|
|
||||||
|
Collection<TickerTask> selectedTasks = cachedTransferList;
|
||||||
|
ticker.requestWork(selectTasks(ticker, tasks, selectedTasks));
|
||||||
|
selectedTasks.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
toDispose.addAll(tasks);
|
||||||
|
tasks.clear();
|
||||||
|
|
||||||
|
logger.debug("Stage started");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<TickerTask> selectTasks(
|
||||||
|
Ticker ticker,
|
||||||
|
Collection<? extends TickerTask> tasks,
|
||||||
|
Collection<TickerTask> output
|
||||||
|
) {
|
||||||
|
// TODO implement properly
|
||||||
|
|
||||||
|
for (TickerTask task : tasks) {
|
||||||
|
// Assign to one ticker randomly
|
||||||
|
if (task.hashCode() % tickers.size() == ticker.getId()) {
|
||||||
|
output.add(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void sync() throws InterruptedException {
|
||||||
|
logger.debug("Starting sync wait");
|
||||||
|
while (workingTickers.get() > 0) {
|
||||||
|
this.wait();
|
||||||
|
logger.debug("Sync notification received");
|
||||||
|
}
|
||||||
|
logger.debug("Sync achieved");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interface for Tickers
|
||||||
|
*/
|
||||||
|
|
||||||
|
synchronized void reportWorkComplete() {
|
||||||
|
int stillWorking = workingTickers.decrementAndGet();
|
||||||
|
if (stillWorking < 0)
|
||||||
|
throw new IllegalStateException("stillWorking = " + stillWorking);
|
||||||
|
|
||||||
|
if (stillWorking != 0) {
|
||||||
|
logger.debug("stillWorking = {}, not notifying sync", stillWorking);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("All tickers reported completion, notifying sync");
|
||||||
|
|
||||||
|
this.notifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void crash(Throwable t, String thread) {
|
||||||
|
if (t instanceof ConcurrentModificationException) {
|
||||||
|
logger.debug("javahorse kill urself");
|
||||||
|
}
|
||||||
|
|
||||||
|
CrashReports.report(
|
||||||
|
t,
|
||||||
|
"Something has gone horribly wrong in server ticker code "
|
||||||
|
+ "(thread %s) and it is (probably) not related to mods or devils.",
|
||||||
|
thread
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.ticking;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.world.Coordinates;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A task that can be executed by a Ticker.
|
||||||
|
* This is a superinterface for {@link Change} and {@link Evaluation} and is not meant to be extended further.
|
||||||
|
* This interface is used to determine the Ticker that is suitable for the execution of this task.
|
||||||
|
* @author javapony
|
||||||
|
*/
|
||||||
|
public abstract class TickerTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code false} iff this task is thread-safe and may be executed by
|
||||||
|
* any Ticker. If and only if a task returns {@code true} in this method
|
||||||
|
* is its {@link #getRelevantChunk(Vec3i)} method invoked.
|
||||||
|
* @implNote Default implementation returns {@code true}, making this task thread-sensitive
|
||||||
|
* @return {@code true} iff this task must be run in a Ticker implied by {@link #getRelevantChunk(Vec3i)}
|
||||||
|
*/
|
||||||
|
public boolean isThreadSensitive() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets {@code output} to be equal to the {@linkplain Coordinates#chunk coordinates of chunk}
|
||||||
|
* of the chunk that must be owned by the Ticker will execute this task. This method
|
||||||
|
* is not invoked iff {@link #isThreadSensitive()} returned {@code false}.
|
||||||
|
* @param output a {@link Vec3i} to set to the requested value
|
||||||
|
*/
|
||||||
|
public abstract void getRelevantChunk(Vec3i output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when this task has completed and will no longer be used.
|
||||||
|
* This method is guaranteed to be invoked in the main server thread.
|
||||||
|
* @implNote Default implementation does nothing
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes this task. This method is provided for the convenience of Tickers.
|
||||||
|
* @param server the server to run on
|
||||||
|
*/
|
||||||
|
abstract void run(Server server);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tile;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
||||||
|
import ru.windcorp.progressia.server.world.block.BlockTickContext;
|
||||||
|
|
||||||
|
public class EdgeTileLogic extends TileLogic implements UpdateableTile {
|
||||||
|
|
||||||
|
public EdgeTileLogic(String id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(TileTickContext context) {
|
||||||
|
if (!canOccupyFace(context)) {
|
||||||
|
context.removeThisTile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canOccupyFace(TileTickContext context) {
|
||||||
|
boolean canOccupy = false;
|
||||||
|
|
||||||
|
BlockTickContext currentTickContext = context.grabCurrentBlockContext();
|
||||||
|
canOccupy ^= canOccupyFace(context, context.getCurrentFace(), currentTickContext);
|
||||||
|
context.release(currentTickContext);
|
||||||
|
|
||||||
|
BlockTickContext counterTickContext = context.grabCounterBlockContext();
|
||||||
|
canOccupy ^= canOccupyFace(context, context.getCounterFace(), counterTickContext);
|
||||||
|
context.release(counterTickContext);
|
||||||
|
|
||||||
|
return canOccupy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canOccupyFace(TileTickContext ownContext, BlockFace blockFace, BlockTickContext blockContext) {
|
||||||
|
BlockLogic block = blockContext.getBlock();
|
||||||
|
if (block == null) return false;
|
||||||
|
|
||||||
|
return block.isSolid(blockContext, ownContext.getCurrentFace());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,17 +1,9 @@
|
|||||||
package ru.windcorp.progressia.server.world.tile;
|
package ru.windcorp.progressia.server.world.tile;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
import ru.windcorp.progressia.common.world.ChunkData;
|
|
||||||
import ru.windcorp.progressia.common.world.WorldData;
|
|
||||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
|
||||||
import ru.windcorp.progressia.server.Server;
|
import ru.windcorp.progressia.server.Server;
|
||||||
import ru.windcorp.progressia.server.world.ChunkLogic;
|
|
||||||
import ru.windcorp.progressia.server.world.WorldLogic;
|
import ru.windcorp.progressia.server.world.WorldLogic;
|
||||||
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
|
||||||
|
|
||||||
public class ForwardingTileTickContext implements TileTickContext {
|
public class ForwardingTileTickContext implements TileTickContext {
|
||||||
|
|
||||||
@ -29,16 +21,6 @@ public class ForwardingTileTickContext implements TileTickContext {
|
|||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChunkLogic getChunk() {
|
|
||||||
return parent.getChunk();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChunkData getChunkData() {
|
|
||||||
return parent.getChunkData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getTickLength() {
|
public double getTickLength() {
|
||||||
return parent.getTickLength();
|
return parent.getTickLength();
|
||||||
@ -55,78 +37,23 @@ public class ForwardingTileTickContext implements TileTickContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WorldData getWorldData() {
|
public Vec3i getCurrentBlockInWorld() {
|
||||||
return parent.getWorldData();
|
return parent.getCurrentBlockInWorld();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requestBlockTick(Vec3i blockInWorld) {
|
public Vec3i getCounterBlockInWorld() {
|
||||||
parent.requestBlockTick(blockInWorld);
|
return parent.getCounterBlockInWorld();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requestTileTick(Vec3i blockInWorld, BlockFace face, int layer) {
|
public BlockFace getCurrentFace() {
|
||||||
parent.requestTileTick(blockInWorld, face, layer);
|
return parent.getCurrentFace();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Vec3i getCoords() {
|
public int getCurrentLayer() {
|
||||||
return parent.getCoords();
|
return parent.getCurrentLayer();
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Vec3i getChunkCoords() {
|
|
||||||
return parent.getChunkCoords();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockFace getFace() {
|
|
||||||
return parent.getFace();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getLayer() {
|
|
||||||
return parent.getLayer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TileLogic getTile() {
|
|
||||||
return parent.getTile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TileData getTileData() {
|
|
||||||
return parent.getTileData();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<TileLogic> getTiles() {
|
|
||||||
return parent.getTiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<TileLogic> getTilesOrNull() {
|
|
||||||
return parent.getTilesOrNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<TileData> getTileDataList() {
|
|
||||||
return parent.getTileDataList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<TileData> getTileDataListOrNull() {
|
|
||||||
return parent.getTileDataListOrNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockLogic getBlock() {
|
|
||||||
return parent.getBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockData getBlockData() {
|
|
||||||
return parent.getBlockData();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
package ru.windcorp.progressia.server.world.tile;
|
package ru.windcorp.progressia.server.world.tile;
|
||||||
|
|
||||||
import ru.windcorp.progressia.server.world.Changer;
|
|
||||||
|
|
||||||
public interface TickableTile {
|
public interface TickableTile {
|
||||||
|
|
||||||
void tick(TileTickContext context, Changer changer);
|
void tick(TileTickContext context);
|
||||||
|
|
||||||
default boolean doesTickRegularly(TileTickContext context) {
|
default boolean doesTickRegularly(TileTickContext context) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -10,7 +10,7 @@ public class TileLogic extends Namespaced {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean canOccupyFace(TileTickContext context) {
|
public boolean canOccupyFace(TileTickContext context) {
|
||||||
return canOccupyFace(context.getFace());
|
return canOccupyFace(context.getCurrentFace());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canOccupyFace(BlockFace face) {
|
public boolean canOccupyFace(BlockFace face) {
|
||||||
|
@ -3,69 +3,158 @@ package ru.windcorp.progressia.server.world.tile;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import glm.vec._3.i.Vec3i;
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.ChunkData;
|
||||||
|
import ru.windcorp.progressia.common.world.Coordinates;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockData;
|
import ru.windcorp.progressia.common.world.block.BlockData;
|
||||||
import ru.windcorp.progressia.common.world.block.BlockFace;
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
import ru.windcorp.progressia.common.world.tile.TileData;
|
import ru.windcorp.progressia.common.world.tile.TileData;
|
||||||
import ru.windcorp.progressia.server.world.ChunkTickContext;
|
import ru.windcorp.progressia.server.world.ChunkLogic;
|
||||||
|
import ru.windcorp.progressia.server.world.TickAndUpdateUtil;
|
||||||
|
import ru.windcorp.progressia.server.world.TickContext;
|
||||||
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
||||||
|
import ru.windcorp.progressia.server.world.block.BlockTickContext;
|
||||||
|
|
||||||
public interface TileTickContext extends ChunkTickContext {
|
public interface TileTickContext extends TickContext {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specifications
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current world coordinates.
|
* Returns the current world coordinates.
|
||||||
* @return the world coordinates of the tile being ticked
|
* @return the world coordinates of the tile being ticked
|
||||||
*/
|
*/
|
||||||
Vec3i getCoords();
|
Vec3i getCurrentBlockInWorld();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current chunk coordinates.
|
* Returns the counter world coordinates.
|
||||||
* @return the chunk coordinates of the tile being ticked
|
* @return the world coordinates of the tile being ticked
|
||||||
*/
|
*/
|
||||||
Vec3i getChunkCoords();
|
Vec3i getCounterBlockInWorld();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current block face. This face is always
|
* Returns the current block face.
|
||||||
* {@linkplain BlockFace#isPrimary() primary}.
|
|
||||||
* @return the block face that the tile being ticked occupies
|
* @return the block face that the tile being ticked occupies
|
||||||
*/
|
*/
|
||||||
BlockFace getFace();
|
BlockFace getCurrentFace();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current layer.
|
* Returns the current layer.
|
||||||
* @return the layer that the tile being ticked occupies in the tile stack
|
* @return the layer that the tile being ticked occupies in the tile stack
|
||||||
*/
|
*/
|
||||||
int getLayer();
|
int getCurrentLayer();
|
||||||
|
|
||||||
|
default BlockFace getCounterFace() {
|
||||||
|
return getCurrentFace().getCounter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tile-related
|
||||||
|
*/
|
||||||
|
|
||||||
default TileLogic getTile() {
|
default TileLogic getTile() {
|
||||||
return getTiles().get(getLayer());
|
return getTiles().get(getCurrentLayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
default TileData getTileData() {
|
default TileData getTileData() {
|
||||||
return getTileDataList().get(getLayer());
|
return getTileDataList().get(getCurrentLayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
default List<TileLogic> getTiles() {
|
default List<TileLogic> getTiles() {
|
||||||
return getChunk().getTiles(getChunkCoords(), getFace());
|
Vec3i blockInChunk = Vectors.grab3i();
|
||||||
|
Coordinates.convertInWorldToInChunk(getCurrentBlockInWorld(), blockInChunk);
|
||||||
|
List<TileLogic> result = getCurrentChunk().getTiles(blockInChunk, getCurrentFace());
|
||||||
|
Vectors.release(blockInChunk);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
default List<TileLogic> getTilesOrNull() {
|
default List<TileLogic> getTilesOrNull() {
|
||||||
return getChunk().getTilesOrNull(getChunkCoords(), getFace());
|
Vec3i blockInChunk = Vectors.grab3i();
|
||||||
|
Coordinates.convertInWorldToInChunk(getCurrentBlockInWorld(), blockInChunk);
|
||||||
|
List<TileLogic> result = getCurrentChunk().getTilesOrNull(blockInChunk, getCurrentFace());
|
||||||
|
Vectors.release(blockInChunk);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
default List<TileData> getTileDataList() {
|
default List<TileData> getTileDataList() {
|
||||||
return getChunkData().getTiles(getChunkCoords(), getFace());
|
Vec3i blockInChunk = Vectors.grab3i();
|
||||||
|
Coordinates.convertInWorldToInChunk(getCurrentBlockInWorld(), blockInChunk);
|
||||||
|
List<TileData> result = getCurrentChunkData().getTiles(blockInChunk, getCurrentFace());
|
||||||
|
Vectors.release(blockInChunk);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
default List<TileData> getTileDataListOrNull() {
|
default List<TileData> getTileDataListOrNull() {
|
||||||
return getChunkData().getTilesOrNull(getChunkCoords(), getFace());
|
Vec3i blockInChunk = Vectors.grab3i();
|
||||||
|
Coordinates.convertInWorldToInChunk(getCurrentBlockInWorld(), blockInChunk);
|
||||||
|
List<TileData> result = getCurrentChunkData().getTilesOrNull(blockInChunk, getCurrentFace());
|
||||||
|
Vectors.release(blockInChunk);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
default BlockLogic getBlock() {
|
/*
|
||||||
return getChunk().getBlock(getChunkCoords());
|
* Current block/chunk
|
||||||
|
*/
|
||||||
|
|
||||||
|
default ChunkLogic getCurrentChunk() {
|
||||||
|
return getWorld().getChunkByBlock(getCurrentBlockInWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
default BlockData getBlockData() {
|
default ChunkData getCurrentChunkData() {
|
||||||
return getChunkData().getBlock(getChunkCoords());
|
return getWorldData().getChunkByBlock(getCurrentBlockInWorld());
|
||||||
|
}
|
||||||
|
|
||||||
|
default BlockLogic getCurrentBlock() {
|
||||||
|
return getWorld().getBlock(getCurrentBlockInWorld());
|
||||||
|
}
|
||||||
|
|
||||||
|
default BlockData getCurrentBlockData() {
|
||||||
|
return getWorldData().getBlock(getCurrentBlockInWorld());
|
||||||
|
}
|
||||||
|
|
||||||
|
default BlockTickContext grabCurrentBlockContext() {
|
||||||
|
return TickAndUpdateUtil.grabBlockTickContext(getServer(), getCurrentBlockInWorld());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Counter block/chunk
|
||||||
|
*/
|
||||||
|
|
||||||
|
default ChunkLogic getCounterChunk() {
|
||||||
|
return getWorld().getChunkByBlock(getCounterBlockInWorld());
|
||||||
|
}
|
||||||
|
|
||||||
|
default ChunkData getCounterChunkData() {
|
||||||
|
return getWorldData().getChunkByBlock(getCounterBlockInWorld());
|
||||||
|
}
|
||||||
|
|
||||||
|
default BlockLogic getCounterBlock() {
|
||||||
|
return getWorld().getBlock(getCounterBlockInWorld());
|
||||||
|
}
|
||||||
|
|
||||||
|
default BlockData getCounterBlockData() {
|
||||||
|
return getWorldData().getBlock(getCounterBlockInWorld());
|
||||||
|
}
|
||||||
|
|
||||||
|
default BlockTickContext grabCounterBlockContext() {
|
||||||
|
return TickAndUpdateUtil.grabBlockTickContext(getServer(), getCounterBlockInWorld());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convenience methods - changes
|
||||||
|
*/
|
||||||
|
|
||||||
|
default void removeThisTile() {
|
||||||
|
getAccessor().removeTile(getCurrentBlockInWorld(), getCurrentFace(), getTileData());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Misc
|
||||||
|
*/
|
||||||
|
|
||||||
|
default void release(BlockTickContext context) {
|
||||||
|
TickAndUpdateUtil.releaseTickContext(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
package ru.windcorp.progressia.server.world.tile;
|
|
||||||
|
|
||||||
import ru.windcorp.progressia.server.world.Changer;
|
|
||||||
|
|
||||||
public interface UpdatableTile {
|
|
||||||
|
|
||||||
void update(TileTickContext context, Changer changer);
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,7 @@
|
|||||||
|
package ru.windcorp.progressia.server.world.tile;
|
||||||
|
|
||||||
|
public interface UpdateableTile {
|
||||||
|
|
||||||
|
void update(TileTickContext context);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package ru.windcorp.progressia.test;
|
||||||
|
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
||||||
|
|
||||||
|
public class TestBlockLogicAir extends BlockLogic {
|
||||||
|
|
||||||
|
public TestBlockLogicAir(String id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSolid(BlockFace face) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTransparent() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -54,7 +54,7 @@ public class TestContent {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
register(new BlockRenderNone("Test:Air"));
|
register(new BlockRenderNone("Test:Air"));
|
||||||
register(new BlockLogic("Test:Air"));
|
register(new TestBlockLogicAir("Test:Air"));
|
||||||
|
|
||||||
register(new BlockData("Test:Dirt"));
|
register(new BlockData("Test:Dirt"));
|
||||||
register(new BlockRenderOpaqueCube("Test:Dirt", getBlockTexture("dirt")));
|
register(new BlockRenderOpaqueCube("Test:Dirt", getBlockTexture("dirt")));
|
||||||
@ -76,19 +76,19 @@ public class TestContent {
|
|||||||
private static void registerTiles() {
|
private static void registerTiles() {
|
||||||
register(new TileData("Test:Grass"));
|
register(new TileData("Test:Grass"));
|
||||||
register(new TileRenderGrass("Test:Grass", getTileTexture("grass_top"), getTileTexture("grass_side")));
|
register(new TileRenderGrass("Test:Grass", getTileTexture("grass_top"), getTileTexture("grass_side")));
|
||||||
register(new TileLogic("Test:Grass"));
|
register(new TestTileLogicGrass("Test:Grass"));
|
||||||
|
|
||||||
register(new TileData("Test:Stones"));
|
register(new TileData("Test:Stones"));
|
||||||
register(new TileRenderSimple("Test:Stones", getTileTexture("stones")));
|
register(new TileRenderSimple("Test:Stones", getTileTexture("stones")));
|
||||||
register(new TileLogic("Test:Stones"));
|
register(new EdgeTileLogic("Test:Stones"));
|
||||||
|
|
||||||
register(new TileData("Test:YellowFlowers"));
|
register(new TileData("Test:YellowFlowers"));
|
||||||
register(new TileRenderSimple("Test:YellowFlowers", getTileTexture("yellow_flowers")));
|
register(new TileRenderSimple("Test:YellowFlowers", getTileTexture("yellow_flowers")));
|
||||||
register(new TileLogic("Test:YellowFlowers"));
|
register(new EdgeTileLogic("Test:YellowFlowers"));
|
||||||
|
|
||||||
register(new TileData("Test:Sand"));
|
register(new TileData("Test:Sand"));
|
||||||
register(new TileRenderSimple("Test:Sand", getTileTexture("sand")));
|
register(new TileRenderSimple("Test:Sand", getTileTexture("sand")));
|
||||||
register(new TileLogic("Test:Sand"));
|
register(new EdgeTileLogic("Test:Sand"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void registerEntities() {
|
private static void registerEntities() {
|
||||||
@ -125,7 +125,7 @@ public class TestContent {
|
|||||||
block = BlockDataRegistry.getInstance().get("Test:Stone");
|
block = BlockDataRegistry.getInstance().get("Test:Stone");
|
||||||
}
|
}
|
||||||
|
|
||||||
server.getAdHocChanger().setBlock(z000, block);
|
server.getWorldAccessor().setBlock(z000, block);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
data.register("Test:BreakBlock", ControlBreakBlockData::new);
|
data.register("Test:BreakBlock", ControlBreakBlockData::new);
|
||||||
@ -222,7 +222,7 @@ public class TestContent {
|
|||||||
|
|
||||||
private static void onBlockBreakReceived(Server server, PacketControl packet, ru.windcorp.progressia.server.comms.Client client) {
|
private static void onBlockBreakReceived(Server server, PacketControl packet, ru.windcorp.progressia.server.comms.Client client) {
|
||||||
Vec3i blockInWorld = ((ControlBreakBlockData) packet.getControl()).getBlockInWorld();
|
Vec3i blockInWorld = ((ControlBreakBlockData) packet.getControl()).getBlockInWorld();
|
||||||
server.getAdHocChanger().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Air"));
|
server.getWorldAccessor().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Air"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void onBlockPlaceTrigger(ControlData control) {
|
private static void onBlockPlaceTrigger(ControlData control) {
|
||||||
@ -238,7 +238,7 @@ public class TestContent {
|
|||||||
private static void onBlockPlaceReceived(Server server, PacketControl packet, ru.windcorp.progressia.server.comms.Client client) {
|
private static void onBlockPlaceReceived(Server server, PacketControl packet, ru.windcorp.progressia.server.comms.Client client) {
|
||||||
Vec3i blockInWorld = ((ControlPlaceBlockData) packet.getControl()).getBlockInWorld();
|
Vec3i blockInWorld = ((ControlPlaceBlockData) packet.getControl()).getBlockInWorld();
|
||||||
if (server.getWorld().getData().getChunkByBlock(blockInWorld) == null) return;
|
if (server.getWorld().getData().getChunkByBlock(blockInWorld) == null) return;
|
||||||
server.getAdHocChanger().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Stone"));
|
server.getWorldAccessor().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Stone"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ru.windcorp.progressia.test;
|
package ru.windcorp.progressia.test;
|
||||||
|
|
||||||
import ru.windcorp.progressia.common.world.entity.EntityData;
|
import ru.windcorp.progressia.common.world.entity.EntityData;
|
||||||
import ru.windcorp.progressia.server.world.Changer;
|
|
||||||
import ru.windcorp.progressia.server.world.TickContext;
|
import ru.windcorp.progressia.server.world.TickContext;
|
||||||
import ru.windcorp.progressia.server.world.entity.EntityLogic;
|
import ru.windcorp.progressia.server.world.entity.EntityLogic;
|
||||||
|
|
||||||
@ -12,13 +11,13 @@ public class TestEntityLogicStatie extends EntityLogic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tick(EntityData entity, TickContext context, Changer changer) {
|
public void tick(EntityData entity, TickContext context) {
|
||||||
super.tick(entity, context, changer);
|
super.tick(entity, context);
|
||||||
|
|
||||||
TestEntityDataStatie statie = (TestEntityDataStatie) entity;
|
TestEntityDataStatie statie = (TestEntityDataStatie) entity;
|
||||||
|
|
||||||
int size = (int) (18 + 6 * Math.sin(entity.getAge()));
|
int size = (int) (18 + 6 * Math.sin(entity.getAge()));
|
||||||
changer.changeEntity(statie, e -> e.setSizeNow(size));
|
context.getServer().getWorldAccessor().changeEntity(statie, e -> e.setSizeNow(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
package ru.windcorp.progressia.test;
|
||||||
|
|
||||||
|
import glm.vec._3.i.Vec3i;
|
||||||
|
import ru.windcorp.progressia.common.util.Vectors;
|
||||||
|
import ru.windcorp.progressia.common.world.block.BlockFace;
|
||||||
|
import ru.windcorp.progressia.server.Server;
|
||||||
|
import ru.windcorp.progressia.server.world.TickAndUpdateUtil;
|
||||||
|
import ru.windcorp.progressia.server.world.block.BlockLogic;
|
||||||
|
import ru.windcorp.progressia.server.world.block.BlockTickContext;
|
||||||
|
import ru.windcorp.progressia.server.world.tile.EdgeTileLogic;
|
||||||
|
import ru.windcorp.progressia.server.world.tile.TileTickContext;
|
||||||
|
|
||||||
|
public class TestTileLogicGrass extends EdgeTileLogic {
|
||||||
|
|
||||||
|
public TestTileLogicGrass(String id) {
|
||||||
|
super(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBlockAboveTransparent(Server server, Vec3i blockInWorld) {
|
||||||
|
Vec3i blockAboveCoords = Vectors.grab3i();
|
||||||
|
blockAboveCoords.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
|
||||||
|
blockAboveCoords.add(BlockFace.TOP.getVector());
|
||||||
|
|
||||||
|
BlockTickContext blockAboveContext = TickAndUpdateUtil.grabBlockTickContext(server, blockAboveCoords);
|
||||||
|
|
||||||
|
try {
|
||||||
|
BlockLogic blockAbove = blockAboveContext.getBlock();
|
||||||
|
if (blockAbove == null) return true;
|
||||||
|
|
||||||
|
return blockAbove.isTransparent(blockAboveContext);
|
||||||
|
} finally {
|
||||||
|
TickAndUpdateUtil.releaseTickContext(blockAboveContext);
|
||||||
|
Vectors.release(blockAboveCoords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(TileTickContext context) {
|
||||||
|
super.update(context);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
context.getCurrentFace() == BlockFace.BOTTOM
|
||||||
|
||
|
||||||
|
isBlockAboveTransparent(context.getServer(), context.getCurrentBlockInWorld())
|
||||||
|
)
|
||||||
|
||
|
||||||
|
!(
|
||||||
|
context.getCounterFace() == BlockFace.BOTTOM
|
||||||
|
||
|
||||||
|
isBlockAboveTransparent(context.getServer(), context.getCounterBlockInWorld())
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
context.removeThisTile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,12 +8,12 @@
|
|||||||
<Appenders>
|
<Appenders>
|
||||||
|
|
||||||
<Console name="Console" target="SYSTEM_OUT">
|
<Console name="Console" target="SYSTEM_OUT">
|
||||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%-20t] %-5level %-32logger{32} > %msg%n"/>
|
||||||
</Console>
|
</Console>
|
||||||
|
|
||||||
<RollingFile name="FileLog" fileName="${APP_LOG_ROOT}/game.log"
|
<RollingFile name="FileLog" fileName="${APP_LOG_ROOT}/game.log"
|
||||||
filePattern="${APP_LOG_ROOT}/game-%d{yyyy-MM-dd}-%i.log">
|
filePattern="${APP_LOG_ROOT}/game-%d{yyyy-MM-dd}-%i.log">
|
||||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%-20t] %-5level %-32logger{32} > %msg%n"/>
|
||||||
<Policies>
|
<Policies>
|
||||||
<SizeBasedTriggeringPolicy size="18MB" />
|
<SizeBasedTriggeringPolicy size="18MB" />
|
||||||
</Policies>
|
</Policies>
|
||||||
@ -21,6 +21,12 @@
|
|||||||
</Appenders>
|
</Appenders>
|
||||||
|
|
||||||
<Loggers>
|
<Loggers>
|
||||||
|
|
||||||
|
<!-- Uncomment to enable Ticker debugging
|
||||||
|
<Logger name="Ticker Coordinator" level="DEBUG" />
|
||||||
|
<Logger name="Ticker 0" level="DEBUG" />
|
||||||
|
-->
|
||||||
|
|
||||||
<Root level="info">
|
<Root level="info">
|
||||||
<AppenderRef ref="FileLog" />
|
<AppenderRef ref="FileLog" />
|
||||||
<AppenderRef ref="Console" />
|
<AppenderRef ref="Console" />
|
||||||
|
Reference in New Issue
Block a user