Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ private void loadManagers() {
private void loadRunnables() {
this.schedulerPool = Executors.newScheduledThreadPool(configManager.getConfig().getInt("models.thread-pool-size", 4));
this.schedulerPool.scheduleAtFixedRate(new UpdateTaskRunnable(this), 10, configManager.getConfig().getLong("models.entity-position-update-period", 35), TimeUnit.MILLISECONDS);
this.schedulerPool.scheduleAtFixedRate(new BedrockMountControlRunnable(this), 1, 1, TimeUnit.MILLISECONDS);
this.schedulerPool.scheduleAtFixedRate(new BedrockMountControlRunnable(this), 1, configManager.getConfig().getLong("models.mount-control-period", 50), TimeUnit.MILLISECONDS);
}

public ConfigManager getConfigManager() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public class EntityTaskManager {

private PropertyHandler propertyHandler;

private volatile List<Player> bedrockPlayers = Collections.emptyList();

public EntityTaskManager(GeyserModelEngine plugin) {
this.plugin = plugin;

Expand All @@ -40,10 +42,22 @@ public EntityTaskManager(GeyserModelEngine plugin) {
}
}

public void checkViewers(EntityData model, Set<Player> viewers) {
/**
* Refreshes the cached list of Bedrock players. Called once per global update cycle instead of
* scanning the online player list from every per-entity task.
*/
public void refreshBedrockPlayers() {
List<Player> players = new ArrayList<>();

for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (!BedrockUtils.isBedrockPlayer(onlinePlayer)) continue;
if (BedrockUtils.isBedrockPlayer(onlinePlayer)) players.add(onlinePlayer);
}

bedrockPlayers = players;
}

public void checkViewers(EntityData model, Set<Player> viewers) {
for (Player onlinePlayer : bedrockPlayers) {
if (canSee(onlinePlayer, model.getEntity(), model.getModelInstance())) {
if (!viewers.contains(onlinePlayer)) {
viewers.add(onlinePlayer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class ModelManager {

private ModelHandler modelHandler;

private final HashSet<UUID> playerJoinedCache = new HashSet<>();
private final Set<UUID> playerJoinedCache = ConcurrentHashMap.newKeySet();

private final ConcurrentHashMap<Integer, Model> modelEntitiesCache = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Integer, Map<Model, EntityData>> entitiesCache = new ConcurrentHashMap<>();
Expand Down Expand Up @@ -56,7 +56,7 @@ public ModelHandler getModelHandler() {
return modelHandler;
}

public HashSet<UUID> getPlayerJoinedCache() {
public Set<UUID> getPlayerJoinedCache() {
return playerJoinedCache;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,18 @@ public BetterModelPropertyHandler(GeyserModelEngine plugin) {

@Override
public void sendScale(EntityData entityData, Collection<Player> players, float lastScale, boolean firstSend) {
if (players.isEmpty()) return;

BetterModelEntityData betterModelEntityData = (BetterModelEntityData) entityData;
Tracker tracker = (Tracker) betterModelEntityData.getModelInstance();
ModelScaler scaler = tracker.scaler();
var scale = scaler.scale(tracker);

if (!firstSend && scale == lastScale) return;
Comment on lines 32 to +40

players.forEach(player -> EntityUtils.sendCustomScale(player, betterModelEntityData.getEntity().getEntityId(), scale));

betterModelEntityData.getEntityTask().setLastScale(scale);
}

@Override
Expand All @@ -44,17 +51,18 @@ public void sendColor(EntityData entityData, Collection<Player> players, Color l
BetterModelEntityData betterModelEntityData = (BetterModelEntityData) entityData;

Color color = new Color(0xFFFFFF);
if (betterModelEntityData.isHurt()) color = new Color(betterModelEntityData.getEntityTracker().damageTintValue());

if (firstSend) {
if (color.equals(lastColor)) return;
if (betterModelEntityData.isHurt()) {
color = new Color(betterModelEntityData.getEntityTracker().damageTintValue());
betterModelEntityData.setHurt(false);
}

if (!firstSend && color.equals(lastColor)) return;

for (Player player : players) {
EntityUtils.sendCustomColor(player, betterModelEntityData.getEntity().getEntityId(), color);
}

betterModelEntityData.setHurt(false);
betterModelEntityData.getEntityTask().setLastColor(color);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public void sendScale(EntityData modelData, Collection<Player> players, float la
}

players.forEach(player -> EntityUtils.sendCustomScale(player, modelEngineEntityData.getEntity().getEntityId(), average));

modelEngineEntityData.getEntityTask().setLastScale(average);
} catch (Exception err) {
throw new RuntimeException(err);
}
Expand All @@ -55,6 +57,8 @@ public void sendColor(EntityData entityData, Collection<Player> players, Color l
if (!firstSend && color.equals(lastColor)) return;

players.forEach(player -> EntityUtils.sendCustomColor(player, data.getEntity().getEntityId(), color));

data.getEntityTask().setLastColor(color);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public BetterModelTaskHandler(GeyserModelEngine plugin, BetterModelEntityData en
} catch (Throwable err) {
err.printStackTrace();
}
}, 0, 20, TimeUnit.MILLISECONDS);
}, 0, plugin.getConfigManager().getConfig().getLong("models.entity-update-period", 20), TimeUnit.MILLISECONDS);
}

@Override
Expand Down Expand Up @@ -84,15 +84,21 @@ public void runAsync() {
}

tick++;

// The first scale/color send after spawn is fired once, and the Geyser side silently drops it
// if the custom entity is not registered yet. Force a resend every ~2s so a model that missed
// that window recovers instead of staying at default scale / no tint forever.
boolean forceSync = tick % 100 == 0;
Comment on lines +88 to +91

if (tick > 400) {
tick = 0;
plugin.getEntityTaskManager().sendHitBoxToAll(entityData);
}

if (viewers.isEmpty()) return;

plugin.getEntityTaskManager().getPropertyHandler().sendScale(entityData, viewers, lastScale, false);
plugin.getEntityTaskManager().getPropertyHandler().sendColor(entityData, viewers, lastColor, false);
plugin.getEntityTaskManager().getPropertyHandler().sendScale(entityData, viewers, lastScale, forceSync);
plugin.getEntityTaskManager().getPropertyHandler().sendColor(entityData, viewers, lastColor, forceSync);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public ModelEngineTaskHandler(GeyserModelEngine plugin, ModelEngineEntityData en
} catch (Throwable err) {
err.printStackTrace();
}
}, 0, 20, TimeUnit.MILLISECONDS);
}, 0, plugin.getConfigManager().getConfig().getLong("models.entity-update-period", 20), TimeUnit.MILLISECONDS);
}

@Override
Expand Down Expand Up @@ -88,15 +88,21 @@ public void runAsync() {
}

tick++;

// The first scale/color send after spawn is fired once, and the Geyser side silently drops it
// if the custom entity is not registered yet. Force a resend every ~2s so a model that missed
// that window recovers instead of staying at default scale / no tint forever.
boolean forceSync = tick % 100 == 0;
Comment on lines +92 to +95

if (tick > 400) {
tick = 0;
plugin.getEntityTaskManager().sendHitBoxToAll(entityData);
}

if (viewers.isEmpty()) return;

plugin.getEntityTaskManager().getPropertyHandler().sendScale(entityData, viewers, lastScale, false);
plugin.getEntityTaskManager().getPropertyHandler().sendColor(entityData, viewers, lastColor, false);
plugin.getEntityTaskManager().getPropertyHandler().sendScale(entityData, viewers, lastScale, forceSync);
plugin.getEntityTaskManager().getPropertyHandler().sendColor(entityData, viewers, lastColor, forceSync);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public BedrockMountControlRunnable(GeyserModelEngine plugin) {
public void run() {
for (UUID playerUUID : plugin.getModelManager().getPlayerJoinedCache()) {
Player player = Bukkit.getPlayer(playerUUID);
if (player == null) continue;

float pitch = player.getLocation().getPitch();
Pair<ActiveModel, Mount> seat = plugin.getModelManager().getDriversCache().get(player.getUniqueId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public UpdateTaskRunnable(GeyserModelEngine plugin) {

@Override
public void run() {
plugin.getEntityTaskManager().refreshBedrockPlayers();

ConcurrentHashMap<Integer, Map<Model, EntityData>> entitiesCache = plugin.getModelManager().getEntitiesCache();
if (entitiesCache.isEmpty()) return;

Expand Down
2 changes: 2 additions & 0 deletions paper/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ models:
custom-entity-sync-resend-interval: 40
custom-entity-sync-resend-count: 3
entity-position-update-period: 35 # ms
entity-update-period: 20 # ms
mount-control-period: 50 # ms
thread-pool-size: 4

options:
Expand Down