Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[submodule "logging"]
path = logging
url = https://github.com/Cubicake/CubiLogging.git
[submodule "leafpile"]
path = leafpile
url = https://github.com/Cubicake/ParallelepipedLeafPile.git
branch = java-21-main
5 changes: 4 additions & 1 deletion core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ dependencies {
compileOnly("org.spongepowered:configurate-core:4.2.0")
compileOnly("org.spongepowered:configurate-yaml:4.2.0")
compileOnly("it.unimi.dsi:fastutil:8.5.18")

implementation(project(":locatable-lib"))

compileOnly(project(":leafpile"))
compileOnly(project(":logging"))

testImplementation(platform("org.junit:junit-bom:5.10.2"))
Expand All @@ -32,7 +35,7 @@ dependencies {
testImplementation("it.unimi.dsi:fastutil:8.5.18")
}

val coreVersion = "0.5.2-SNAPSHOT"
val coreVersion = "0.5.4-SNAPSHOT"

val isRelease = gradle.startParameter.taskNames.any {
it.contains("buildRelease")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package games.cubi.raycastedantiesp.core.engine;

import games.cubi.locatables.ImmutableLocatable;
import games.cubi.locatables.Locatable;
import games.cubi.logs.Logger;
import games.cubi.raycastedantiesp.core.config.ConfigManager;
Expand Down Expand Up @@ -324,6 +325,9 @@ private void processTickForPlayers(List<PlayerData> playerDataList, EntityConfig
boolean debugParticles, int currentTick, TickTimingBatch timings) {

for (PlayerData playerData : playerDataList) {
if (!playerData.isConnected()) {
continue;
}
playerData.nettyData().markPendingPostSpawnTasksForEviction();
if (playerData.hasBypassPermission()) {
timings.incrementBypassSkippedPlayers();
Expand All @@ -333,7 +337,7 @@ private void processTickForPlayers(List<PlayerData> playerDataList, EntityConfig
BlockView blockView = playerData.blockView();

Locatable playerLocation = playerData.ownLocation();
if (playerLocation == null) {
if (playerLocation == null || playerLocation.world() == null) {
timings.incrementNullLocationSkippedPlayers();
continue;
}
Expand All @@ -360,57 +364,52 @@ private void processTickForPlayers(List<PlayerData> playerDataList, EntityConfig
private void checkEntities(PlayerData player, Locatable playerLocation, EntityConfig entityConfig, boolean debugParticles, BlockView blockView, int currentTick, TickTimingBatch timings) {
EntityView<?> entityView = player.entityView();

int checked = entityView.forEachNeedingRecheck(entityConfig.getVisibleRecheckIntervalTicks(), currentTick, entityUUID -> {
boolean wasVisible = entityView.isVisible(entityUUID);
//todo Instead of passing by UUID reference everywhere (attachedToSelf and getLocation) have forEachNeedingRecheck pass the entity reference itself.
if (attachedToSelf(player, entityView, entityUUID, currentTick)) {
int checked = entityView.forEachNeedingRecheckEntity(entityConfig.getVisibleRecheckIntervalTicks(), currentTick, !(timings instanceof TickTimingBatchNoOp), entity -> {
boolean wasVisible = entity.visible();
if (attachedToSelf(player, entityView, entity, currentTick)) {
return;
}
Locatable entityLocation = entityView.getLocation(entityUUID);
ImmutableLocatable entityLocation = entity.getOffsetEntityLocation();
if (entityLocation == null) {
timings.incrementEntityNullTargets();
Logger.debug("SimpleEngine.checkEntities skipped-null-location viewer=" + player.getPlayerUUID()
+ " target=" + entityUUID
+ " target=" + entity.entityUUID()
+ " wasVisible=" + wasVisible
+ " tick=" + currentTick);
return;
}
timings.incrementEntityRaycasts();
boolean canSee = RaycastUtil.raycast(player, playerLocation, entityLocation, entityConfig.getMaxOccludingCount(), entityConfig.getAlwaysShowRadius(), entityConfig.getRaycastRadius(), debugParticles, blockView, 1, particleSpawner);
entityView.setVisibility(entityUUID, canSee, currentTick);
entityView.setVisibility(entity, canSee, currentTick);
});
timings.addEntityChecked(checked);
}

private void checkPlayers(PlayerData player, Locatable playerLocation, PlayerConfig playerConfig, boolean debugParticles, BlockView blockView, int currentTick, TickTimingBatch timings) {
EntityView<?> playerView = player.playerView();

int checked = playerView.forEachNeedingRecheck(playerConfig.getVisibleRecheckIntervalTicks(), currentTick, otherPlayerUUID -> {
boolean wasVisible = playerView.isVisible(otherPlayerUUID);
if (attachedToSelf(player, playerView, otherPlayerUUID, currentTick)) {
int checked = playerView.forEachNeedingRecheckEntity(playerConfig.getVisibleRecheckIntervalTicks(), currentTick, !(timings instanceof TickTimingBatchNoOp), otherPlayer -> {
boolean wasVisible = otherPlayer.visible();
if (attachedToSelf(player, playerView, otherPlayer, currentTick)) {
return;
}
Locatable otherPlayerLocation = playerView.getLocation(otherPlayerUUID);
ImmutableLocatable otherPlayerLocation = otherPlayer.getOffsetEntityLocation();
if (otherPlayerLocation == null) {
timings.incrementPlayerNullTargets();
Logger.debug("SimpleEngine.checkPlayers skipped-null-location viewer=" + player.getPlayerUUID()
+ " target=" + otherPlayerUUID
+ " target=" + otherPlayer.entityUUID()
+ " wasVisible=" + wasVisible
+ " tick=" + currentTick);
return;
}
timings.incrementPlayerRaycasts();
boolean canSee = RaycastUtil.raycast(player, playerLocation, otherPlayerLocation, playerConfig.getMaxOccludingCount(), playerConfig.getAlwaysShowRadius(), playerConfig.getRaycastRadius(), debugParticles, blockView, 1, particleSpawner);
playerView.setVisibility(otherPlayerUUID, canSee, currentTick);
playerView.setVisibility(otherPlayer, canSee, currentTick);
});
timings.addPlayerChecked(checked);
}

private boolean attachedToSelf(PlayerData player, EntityView<?> view, UUID entityUUID, int currentTick) {
if (!(view.getEntity(entityUUID) instanceof NettyEntityLocatable<?, ?> entity)) {
Logger.error(new RuntimeException("EntityView returned a non-NettyEntityLocatable for UUID=" + entityUUID + " when checking for self-attachment. This should never happen."), 1, SimpleEngine.class);
return false;
}
private boolean attachedToSelf(PlayerData player, EntityView<?> view, NettyEntityLocatable<?,?> entity, int currentTick) {
int selfEntityID = player.nettyData().getSelfEntityID();
if (!player.nettyData().isSelfEntityID(entity.leashingEntity())
&& !player.nettyData().isSelfEntityID(entity.vehicleID())
Expand All @@ -422,20 +421,19 @@ private boolean attachedToSelf(PlayerData player, EntityView<?> view, UUID entit
}

private void checkTileEntities(PlayerData player, Locatable playerLocation, TileEntityConfig tileEntityConfig, boolean debugParticles, BlockView blockView, int currentTick, TickTimingBatch timings) {
int checked = blockView.forEachNeedingRecheck(tileEntityConfig.getVisibleRecheckIntervalTicks(), currentTick, tileEntityLocation -> {
int checked = blockView.updateVisibilityForEachNeedingRecheck(tileEntityConfig.getVisibleRecheckIntervalTicks(), currentTick, tileEntityLocation -> {
if (tileEntityLocation.world() == null || !tileEntityLocation.world().equals(playerLocation.world())) {
timings.incrementTileWorldSkipped();
return;
return BlockView.VisibilityResolver.SKIPPED;
}

if (playerLocation.distanceSquared(tileEntityLocation) > (double) tileEntityConfig.getRaycastRadius() * tileEntityConfig.getRaycastRadius()) {
timings.incrementTileRadiusSkipped();
blockView.setVisibility(tileEntityLocation, false, currentTick);
return;
return BlockView.VisibilityResolver.HIDE;
}
timings.incrementTileRaycasts();
boolean canSee = RaycastUtil.raycast(player, playerLocation, tileEntityLocation, tileEntityConfig.getMaxOccludingCount() + 1, tileEntityConfig.getAlwaysShowRadius(), tileEntityConfig.getRaycastRadius(), debugParticles, blockView, 1, particleSpawner);
blockView.setVisibility(tileEntityLocation, canSee, currentTick);
return canSee ? BlockView.VisibilityResolver.SHOW : BlockView.VisibilityResolver.HIDE;
});
timings.addTileChecked(checked);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package games.cubi.raycastedantiesp.core.locatables;

import games.cubi.locatables.ImmutableLocatable;
import games.cubi.locatables.MutableLocatable;
import org.jetbrains.annotations.Nullable;

Expand Down Expand Up @@ -62,6 +63,8 @@ public interface EntityLocatable<EntityType, PacketReplayData> extends MutableLo
PacketReplayData packetReplayData();
EntityLocatable<?, ?> setPacketReplayData(PacketReplayData packetReplayData);

ImmutableLocatable getOffsetEntityLocation();

/**
* For use when the player disconnects, clears all data.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package games.cubi.raycastedantiesp.core.locatables;

import games.cubi.locatables.Locatable;
import games.cubi.locatables.ImmutableLocatable;
import games.cubi.locatables.MutableLocatable;
import games.cubi.locatables.implementations.ImmutableLocatableImpl;
import games.cubi.locatables.implementations.MutableLocatableImpl;
import games.cubi.raycastedantiesp.core.players.PlayerData;
import games.cubi.raycastedantiesp.core.utils.Clearable;
Expand Down Expand Up @@ -33,10 +34,10 @@ public abstract class NettyEntityLocatable<EntityType, PacketReplayData extends
private volatile double velocityY;
private volatile double velocityZ;
private volatile boolean onGround = true;
private int@IntArrayListMarker[] leashedIDs;
private int leasherID = NO_LEASHER;
private int[] passengerIDs;
private int vehicleID = NO_VEHICLE;
private volatile int@IntArrayListMarker[] leashedIDs;
private volatile int leasherID = NO_LEASHER;
private volatile int[] passengerIDs;
private volatile int vehicleID = NO_VEHICLE;


private volatile int entityData;
Expand Down Expand Up @@ -287,6 +288,11 @@ public PacketReplayData packetReplayData() {
return this;
}

@Override
public ImmutableLocatable getOffsetEntityLocation() {
return new ImmutableLocatableImpl(world, x, y + 0.5, z); //todo: move away from hardcoded offset
}

@Override
public double x() {
return x;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
*/
public class NettyData implements Clearable {
private static final int DEFAULT_MAP_SIZE = 16;
public static final int NO_SELF_ENTITY_ID = -1;
//
// ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// START Leash tracking:
Expand Down Expand Up @@ -250,28 +249,24 @@ public void evictOldPendingPostSpawnTasks(int currentTick) {
// ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// START Self entity tracking:
//
private NettyEntityLocatable<?, ?> selfEntity;
private int selfEntityID = NO_SELF_ENTITY_ID;
private final NettyEntityLocatable<?, ?> selfEntity;
private final int selfEntityID;

public NettyEntityLocatable<?, ?> getSelfEntity() {
return selfEntity;
public NettyData(NettyEntityLocatable<?, ?> selfEntity) {
this.selfEntity = selfEntity;
this.selfEntityID = selfEntity.entityID();
}

public NettyData setSelfEntity(NettyEntityLocatable<?, ?> selfEntity) {
if (this.selfEntity != null && this.selfEntity != selfEntity) {
this.selfEntity.clear();
}
this.selfEntity = selfEntity;
selfEntityID = selfEntity == null ? NO_SELF_ENTITY_ID : selfEntity.entityID();
return this;
public NettyEntityLocatable<?, ?> getSelfEntity() {
return selfEntity;
}

public int getSelfEntityID() {
return selfEntityID;
}

public boolean isSelfEntityID(int entityID) {
return selfEntityID != NO_SELF_ENTITY_ID && entityID == selfEntityID;
return entityID == selfEntityID;
}
//
// END Self entity tracking.
Expand Down Expand Up @@ -360,8 +355,6 @@ public void clear() {
if (selfEntity != null) {
selfEntity.clear();
}
selfEntity = null;
selfEntityID = NO_SELF_ENTITY_ID;
currentWorldMinHeight = Integer.MIN_VALUE;
currentWorldName = null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,27 +15,30 @@ public class PlayerData {
private final UUID playerUUID;
private final int joinTick;
private volatile boolean hasBypassPermission;
private volatile boolean connected = true;
private final ThreadSafeLocatable ownLocation;

private final BlockView blockView;
private final EntityView<?> entityView;
private final EntityView<?> playerView;
private final NettyData nettyData;

public PlayerData(UUID player, boolean hasBypassPermission, int joinTick) {
this(player, joinTick);
this.hasBypassPermission = hasBypassPermission;
}

public PlayerData(UUID player, int joinTick) {
PlayerData(UUID player, boolean hasBypassPermission, int joinTick, int selfEntityID, PlayerRegistry.SelfEntityCreator selfEntityCreator) {
this.joinTick = joinTick;
this.playerUUID = player;
this.hasBypassPermission = hasBypassPermission;

blockView = ViewRegistry.createBlockView();
entityView = ViewRegistry.createEntityView();
playerView = ViewRegistry.createPlayerEntityView();
nettyData = new NettyData();
ownLocation = new ThreadSafeLocatable(null, 0, 0, 0);
NettyEntityLocatable<?, ?> selfEntity = Logger.requireNonNull(
selfEntityCreator.createSelfEntity(this, selfEntityID, player),
"Self entity creator returned null",
3,
PlayerData.class
);
nettyData = new NettyData(selfEntity);
}

public EntityView<?> entityView() {
Expand Down Expand Up @@ -75,6 +78,14 @@ public int getJoinTick() {
return joinTick;
}

public boolean isConnected() {
return connected;
}

public void markDisconnected() {
connected = false;
}

/**
* @return Either the entity or player view for this player, depending on the entity ID
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package games.cubi.raycastedantiesp.core.players;

import games.cubi.locatables.Locatable;
import games.cubi.raycastedantiesp.core.locatables.NettyEntityLocatable;

import java.util.Collection;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

public class PlayerRegistry {

@FunctionalInterface
public interface SelfEntityCreator {
NettyEntityLocatable<?, ?> createSelfEntity(PlayerData playerData, int selfEntityID, UUID playerUUID);
}

private static PlayerRegistry instance;

private PlayerRegistry() {}
Expand All @@ -19,32 +27,20 @@ public static PlayerRegistry getInstance() {

private final ConcurrentHashMap<UUID, PlayerData> playerDataMap = new ConcurrentHashMap<>();

public void registerPlayerIfAbsent(UUID playerUUID, boolean hasBypassPermission, int joinTick) {
playerDataMap.putIfAbsent(playerUUID, new PlayerData(playerUUID, hasBypassPermission, joinTick));
}

/** Forcefully registers a player and returns the new PlayerData, even if they were already registered.**/
public PlayerData registerAndGetPlayer(UUID playerUUID, int joinTick) {
PlayerData newData = new PlayerData(playerUUID, joinTick);
playerDataMap.put(playerUUID, newData);
public PlayerData registerAndGetPlayer(UUID playerUUID, int joinTick, int selfEntityID, SelfEntityCreator selfEntityCreator) {
PlayerData newData = new PlayerData(playerUUID, false, joinTick, selfEntityID, selfEntityCreator);
PlayerData old = playerDataMap.put(playerUUID, newData);
if (old != null) old.markDisconnected();
return newData;
}

public PlayerData registerAndGetPlayerIfAbsent(UUID playerUUID, boolean hasBypassPermission, int joinTick) {
PlayerData newData = new PlayerData(playerUUID, hasBypassPermission, joinTick);
PlayerData existingData = playerDataMap.putIfAbsent(playerUUID, newData);
return existingData != null ? existingData : newData;
}

public void unregisterPlayer(UUID playerUUID) {
PlayerData unregisteredPlayer = playerDataMap.remove(playerUUID);
if (unregisteredPlayer == null) {
return;
}
unregisteredPlayer.blockView().clear();
unregisteredPlayer.entityView().clear();
unregisteredPlayer.playerView().clear();
unregisteredPlayer.nettyData().clear();
unregisteredPlayer.markDisconnected();
}

public PlayerData getPlayerData(UUID playerUUID) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package games.cubi.raycastedantiesp.core.utils;

/**
* Classes where specific methods must be called from a single thread can extend this class and call {@link #guardThread()}
* at the start of those methods to ensure that they are only called from the permitted thread.
*/
public abstract class SingleThreadedGuard {
private final Thread permittedThread;

protected SingleThreadedGuard(Thread thread) {
permittedThread = thread;
}

protected void guardThread() {
if (Thread.currentThread() != permittedThread) {
throw new IllegalStateException("Method called from wrong thread. Expected: " + permittedThread + ", actual: " + Thread.currentThread());
}
}
}
Loading