diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5c4480e..581256c 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -4,6 +4,7 @@ on:
push:
branches:
- main
+ - bettermodel-support-dev
jobs:
build:
@@ -30,7 +31,7 @@ jobs:
- name: Auto release
uses: "marvinpinto/action-automatic-releases@latest"
with:
- repo_token: "${{ secrets.GITHUB_TOKEN }}"
+ repo_token: "${{secrets.GITHUB_TOKEN}}"
automatic_release_tag: latest
prerelease: false
files: |
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 1b61d9c..9a89915 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -4,16 +4,14 @@
-
-
-
+
-
+
@@ -82,15 +80,20 @@
{
"keyToString": {
+ "Gradle.Build GeyserModelEngine.executor": "Run",
"Gradle.Download Sources.executor": "Run",
+ "Gradle.GeyserModelEngine [buildDependents].executor": "Run",
+ "Gradle.GeyserModelEngine [buildNeeded].executor": "Run",
"Gradle.GeyserModelEngine [build].executor": "Run",
+ "Gradle.GeyserModelEngine [clean].executor": "Run",
"Gradle.GeyserModelEngine [jar].executor": "Run",
"Maven.GeyserModelEngine [install...].executor": "Run",
"Maven.GeyserModelEngine [install].executor": "Run",
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
+ "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
- "git-widget-placeholder": "main",
+ "git-widget-placeholder": "bettermodel-support-dev",
"kotlin-language-version-configured": "true",
"last_opened_file_path": "D:/Coding/Forks/Minecraft/GeyserModelEngine",
"project.structure.last.edited": "Project",
@@ -102,14 +105,57 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+ false
+
+
@@ -132,6 +178,28 @@
false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+ false
+
+
@@ -156,8 +224,11 @@
-
+
+
+
+
diff --git a/build.gradle.kts b/build.gradle.kts
index 287f7a5..ab2d265 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -24,11 +24,12 @@ dependencies {
implementation("dev.jorel:commandapi-paper-shade:11.0.0")
compileOnly("com.ticxo.modelengine:ModelEngine:R4.0.9")
+ compileOnly("io.github.toxicity188:bettermodel:1.14.0")
compileOnly(files("libs/geyserutils-spigot-1.0-SNAPSHOT.jar"))
compileOnly("org.geysermc.floodgate:api:2.2.4-SNAPSHOT")
- implementation("com.github.retrooper:packetevents-spigot:2.10.0")
+ implementation("com.github.retrooper:packetevents-spigot:2.10.1")
implementation("org.bstats:bstats-bukkit:3.0.2")
implementation("org.reflections:reflections:0.10.2")
diff --git a/src/main/java/re/imc/geysermodelengine/Events/GeyserModelEngineModelSpawn.java b/src/main/java/re/imc/geysermodelengine/Events/GeyserModelEngineModelSpawn.java
deleted file mode 100644
index a69eb63..0000000
--- a/src/main/java/re/imc/geysermodelengine/Events/GeyserModelEngineModelSpawn.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package re.imc.geysermodelengine.Events;
-
-public class GeyserModelEngineModelSpawn {
-
- //Spawn Event
-}
diff --git a/src/main/java/re/imc/geysermodelengine/GeyserModelEngine.java b/src/main/java/re/imc/geysermodelengine/GeyserModelEngine.java
index bf8ebee..f2d3a3c 100644
--- a/src/main/java/re/imc/geysermodelengine/GeyserModelEngine.java
+++ b/src/main/java/re/imc/geysermodelengine/GeyserModelEngine.java
@@ -3,6 +3,7 @@ package re.imc.geysermodelengine;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.event.PacketListenerPriority;
import dev.jorel.commandapi.CommandAPI;
+import dev.jorel.commandapi.CommandAPIBukkitConfig;
import dev.jorel.commandapi.CommandAPIPaperConfig;
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
import org.bstats.bukkit.Metrics;
@@ -53,10 +54,9 @@ public class GeyserModelEngine extends JavaPlugin {
@Override
public void onDisable() {
- PacketEvents.getAPI().terminate();
-
this.modelManager.removeEntities();
+ PacketEvents.getAPI().terminate();
CommandAPI.onDisable();
}
@@ -66,7 +66,7 @@ public class GeyserModelEngine extends JavaPlugin {
}
private void loadBStats() {
- if (configManager.getConfig().getBoolean("bstats", true)) new Metrics(this, 26981);
+ if (configManager.getConfig().getBoolean("metrics.bstats", true)) new Metrics(this, 26981);
}
private void loadManagers() {
@@ -79,9 +79,9 @@ public class GeyserModelEngine extends JavaPlugin {
}
private void loadRunnables() {
- this.schedulerPool = Executors.newScheduledThreadPool(configManager.getConfig().getInt("thread-pool-size", 4));
+ this.schedulerPool = Executors.newScheduledThreadPool(configManager.getConfig().getInt("models.thread-pool-size", 4));
- Bukkit.getAsyncScheduler().runAtFixedRate(this, new UpdateTaskRunnable(this), 10, configManager.getConfig().getLong("entity-position-update-period", 35), TimeUnit.MILLISECONDS);
+ Bukkit.getAsyncScheduler().runAtFixedRate(this, new UpdateTaskRunnable(this), 10, configManager.getConfig().getLong("models.entity-position-update-period", 35), TimeUnit.MILLISECONDS);
Bukkit.getAsyncScheduler().runAtFixedRate(this, new BedrockMountControlRunnable(this), 1, 1, TimeUnit.MILLISECONDS);
}
diff --git a/src/main/java/re/imc/geysermodelengine/listener/BetterModelListener.java b/src/main/java/re/imc/geysermodelengine/listener/BetterModelListener.java
new file mode 100644
index 0000000..fb7046e
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/listener/BetterModelListener.java
@@ -0,0 +1,38 @@
+package re.imc.geysermodelengine.listener;
+
+import kr.toxicity.model.api.event.CreateEntityTrackerEvent;
+
+import org.bukkit.entity.Entity;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+import re.imc.geysermodelengine.GeyserModelEngine;
+import re.imc.geysermodelengine.managers.model.entity.BetterModelEntityData;
+import re.imc.geysermodelengine.managers.model.model.Model;
+
+public class BetterModelListener implements Listener {
+
+ private final GeyserModelEngine plugin;
+
+ public BetterModelListener(GeyserModelEngine plugin) {
+ this.plugin = plugin;
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onModelSpawn(CreateEntityTrackerEvent event) {
+ plugin.getModelManager().getModelHandler().createModel(event.sourceEntity(), event.getTracker(), event.tracker());
+ }
+
+ @EventHandler
+ public void onModelDamage(EntityDamageByEntityEvent event) {
+ Entity entity = event.getEntity();
+
+ Model model = plugin.getModelManager().getModelEntitiesCache().get(entity.getEntityId());
+ if (model == null) return;
+
+ BetterModelEntityData entityData = (BetterModelEntityData) model.getEntityData();
+
+ entityData.setHurt(true);
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/listener/ModelEngineListener.java b/src/main/java/re/imc/geysermodelengine/listener/ModelEngineListener.java
new file mode 100644
index 0000000..c0c35af
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/listener/ModelEngineListener.java
@@ -0,0 +1,58 @@
+package re.imc.geysermodelengine.listener;
+
+import com.ticxo.modelengine.api.events.*;
+import com.ticxo.modelengine.api.model.ActiveModel;
+import org.apache.commons.lang3.tuple.Pair;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import re.imc.geysermodelengine.GeyserModelEngine;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.managers.model.model.Model;
+
+import java.util.Map;
+
+public class ModelEngineListener implements Listener {
+
+ private final GeyserModelEngine plugin;
+
+ public ModelEngineListener(GeyserModelEngine plugin) {
+ this.plugin = plugin;
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onAddModel(AddModelEvent event) {
+ if (event.isCancelled()) return;
+ plugin.getModelManager().getModelHandler().createModel(event.getTarget(), event.getModel());
+ }
+
+ // Needs Testing
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onModelMount(ModelMountEvent event) {
+ if (!event.isDriver()) return;
+
+ ActiveModel activeModel = event.getVehicle();
+ if (activeModel == null) return;
+
+ int entityID = activeModel.getModeledEntity().getBase().getEntityId();
+
+ Map entityDataCache = plugin.getModelManager().getEntitiesCache().get(entityID);
+ if (entityDataCache == null) return;
+
+ Model model = plugin.getModelManager().getModelEntitiesCache().get(entityID);
+
+ EntityData entityData = entityDataCache.get(model);
+
+ if (entityData != null && event.getPassenger() instanceof Player player) {
+ plugin.getModelManager().getDriversCache().put(player.getUniqueId(), Pair.of(event.getVehicle(), event.getSeat()));
+ }
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onModelDismount(ModelDismountEvent event) {
+ if (event.getPassenger() instanceof Player player) {
+ plugin.getModelManager().getDriversCache().remove(player.getUniqueId());
+ }
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/listener/ModelListener.java b/src/main/java/re/imc/geysermodelengine/listener/ModelListener.java
index 6f00cc8..dfc268b 100644
--- a/src/main/java/re/imc/geysermodelengine/listener/ModelListener.java
+++ b/src/main/java/re/imc/geysermodelengine/listener/ModelListener.java
@@ -1,22 +1,15 @@
package re.imc.geysermodelengine.listener;
-import com.ticxo.modelengine.api.events.*;
-import com.ticxo.modelengine.api.model.ActiveModel;
-import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
-import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.world.WorldInitEvent;
import org.geysermc.floodgate.api.FloodgateApi;
import re.imc.geysermodelengine.GeyserModelEngine;
-import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
-
-import java.util.Map;
public class ModelListener implements Listener {
@@ -26,41 +19,19 @@ public class ModelListener implements Listener {
this.plugin = plugin;
}
- @EventHandler(priority = EventPriority.MONITOR)
- public void onAddModel(AddModelEvent event) {
- if (event.isCancelled()) return;
- plugin.getModelManager().create(event.getTarget(), event.getModel());
- }
-
- @EventHandler(priority = EventPriority.MONITOR)
- public void onModelMount(ModelMountEvent event) {
- Map map = plugin.getModelManager().getEntitiesCache().get(event.getVehicle().getModeledEntity().getBase().getEntityId());
- if (!event.isDriver()) return;
-
- ModelEntityData model = map.get(event.getVehicle());
-
- if (model != null && event.getPassenger() instanceof Player player) {
- plugin.getModelManager().getDriversCache().put(player.getUniqueId(), Pair.of(event.getVehicle(), event.getSeat()));
- }
- }
-
- @EventHandler(priority = EventPriority.MONITOR)
- public void onModelDismount(ModelDismountEvent event) {
- if (event.getPassenger() instanceof Player player) {
- plugin.getModelManager().getDriversCache().remove(player.getUniqueId());
- }
- }
-
+ /*
+ / xSquishyLiam:
+ / May change this into a better system?
+ */
@EventHandler
public void onWorldInit(WorldInitEvent event) {
World world = event.getWorld();
- world.getEntities().forEach(entity -> plugin.getModelManager().processEntities(entity));
+ world.getEntities().forEach(entity -> plugin.getModelManager().getModelHandler().processEntities(entity));
}
/*
- / xSquishyLiam - conclusion:
- / I'm assuming when a player joins the server the packet for mob spawning is instant so the client resyncs itself
- / hence why the pig is shown instead of going invisible and not displaying the texture of the modeled mob
+ / xSquishyLiam:
+ / A runDelay makes sure the client doesn't see pigs on login due to the client resyncing themselves back to normal
*/
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
diff --git a/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManager.java b/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManager.java
index fe46a7a..a1f76d7 100644
--- a/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManager.java
+++ b/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManager.java
@@ -21,8 +21,8 @@ public class CommandManager {
for (Class> clazz : new Reflections(path).getSubTypesOf(CommandManagers.class)) {
try {
CommandManagers commandManager = (CommandManagers) clazz.getDeclaredConstructor(GeyserModelEngine.class).newInstance(plugin);
- plugin.getLogger().warning("Loading Command Manager - " + commandManager.name());
- commandManagersCache.put(commandManager.name(), commandManager);
+ plugin.getLogger().info("Loading Command Manager - " + commandManager.getName());
+ commandManagersCache.put(commandManager.getName(), commandManager);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException err) {
plugin.getLogger().severe("Failed to load Command Manager " + clazz.getName());
throw new RuntimeException(err);
diff --git a/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManagers.java b/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManagers.java
index d757bbc..89e2ee2 100644
--- a/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManagers.java
+++ b/src/main/java/re/imc/geysermodelengine/managers/commands/CommandManagers.java
@@ -6,7 +6,13 @@ import java.util.ArrayList;
public interface CommandManagers {
- String name();
+ /**
+ * Gets the name of the command manager
+ */
+ String getName();
+ /**
+ * Gets the command manager subcommands
+ */
ArrayList getCommands();
}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.java b/src/main/java/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.java
index 3d13b29..37b0a7d 100644
--- a/src/main/java/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.java
+++ b/src/main/java/re/imc/geysermodelengine/managers/commands/managers/geysermodelengine/GeyserModelEngineCommandManager.java
@@ -19,7 +19,7 @@ public class GeyserModelEngineCommandManager implements CommandManagers {
}
private void registerCommand() {
- CommandAPICommand geyserModelEngineCommand = new CommandAPICommand(name());
+ CommandAPICommand geyserModelEngineCommand = new CommandAPICommand(getName());
commands.forEach(subCommands -> geyserModelEngineCommand.withSubcommand(subCommands.onCommand()));
@@ -27,7 +27,7 @@ public class GeyserModelEngineCommandManager implements CommandManagers {
}
@Override
- public String name() {
+ public String getName() {
return "geysermodelengine";
}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.java b/src/main/java/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.java
index ebdc2a0..3d336fb 100644
--- a/src/main/java/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.java
+++ b/src/main/java/re/imc/geysermodelengine/managers/commands/subcommands/SubCommands.java
@@ -3,5 +3,9 @@ package re.imc.geysermodelengine.managers.commands.subcommands;
import dev.jorel.commandapi.CommandAPICommand;
public interface SubCommands {
+
+ /**
+ * Subcommand setup
+ */
CommandAPICommand onCommand();
}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/EntityTaskManager.java b/src/main/java/re/imc/geysermodelengine/managers/model/EntityTaskManager.java
index 26d5eed..f424698 100644
--- a/src/main/java/re/imc/geysermodelengine/managers/model/EntityTaskManager.java
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/EntityTaskManager.java
@@ -1,84 +1,45 @@
package re.imc.geysermodelengine.managers.model;
import com.ticxo.modelengine.api.animation.BlueprintAnimation;
-import com.ticxo.modelengine.api.generator.blueprint.BlueprintBone;
import com.ticxo.modelengine.api.model.ActiveModel;
-import com.ticxo.modelengine.api.model.render.DisplayRenderer;
import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.geysermc.floodgate.api.FloodgateApi;
-import org.joml.Vector3fc;
import re.imc.geysermodelengine.GeyserModelEngine;
-import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.managers.model.propertyhandler.BetterModelPropertyHandler;
+import re.imc.geysermodelengine.managers.model.propertyhandler.ModelEnginePropertyHandler;
+import re.imc.geysermodelengine.managers.model.propertyhandler.PropertyHandler;
+import re.imc.geysermodelengine.managers.model.entity.ModelEngineEntityData;
+import re.imc.geysermodelengine.managers.model.taskshandler.TaskHandler;
import re.imc.geysermodelengine.packet.entity.PacketEntity;
-import re.imc.geysermodelengine.runnables.EntityTaskRunnable;
-import java.awt.*;
-import java.lang.reflect.Method;
import java.util.*;
public class EntityTaskManager {
private final GeyserModelEngine plugin;
- private final Method scaleMethod;
+ private PropertyHandler propertyHandler;
public EntityTaskManager(GeyserModelEngine plugin) {
this.plugin = plugin;
- try {
- this.scaleMethod = ActiveModel.class.getMethod("getScale");
- } catch (NoSuchMethodException err) {
- throw new RuntimeException(err);
+ if (Bukkit.getPluginManager().getPlugin("ModelEngine") != null) {
+ this.propertyHandler = new ModelEnginePropertyHandler(plugin);
+ plugin.getLogger().info("Using ModelEngine property handler!");
+ } else if (Bukkit.getPluginManager().getPlugin("BetterModel") != null) {
+ this.propertyHandler = new BetterModelPropertyHandler(plugin);
+ plugin.getLogger().info("Using BetterModel property handler!");
+ } else {
+ plugin.getLogger().severe("No supported model engine found!");
+ plugin.getServer().getPluginManager().disablePlugin(plugin);
}
}
- public String unstripName(BlueprintBone bone) {
- String name = bone.getName();
- if (bone.getBehaviors().get("head") != null) {
- if (!bone.getBehaviors().get("head").isEmpty()) return "hi_" + name;
- return "h_" + name;
- }
-
- return name;
- }
-
- public void sendScale(ModelEntityData model, Collection players, float lastScale, boolean firstSend) {
- try {
- if (players.isEmpty()) return;
-
- Vector3fc scale = (Vector3fc) scaleMethod.invoke(model.getActiveModel());
-
- float average = (scale.x() + scale.y() + scale.z()) / 3;
-
- if (!firstSend) {
- if (average == lastScale) return;
- }
-
- for (Player player : players) {
- EntityUtils.sendCustomScale(player, model.getEntity().getEntityId(), average);
- }
- } catch (Throwable ignored) {}
- }
-
- public void sendColor(ModelEntityData model, Collection players, Color lastColor, boolean firstSend) {
- if (players.isEmpty()) return;
-
- Color color = new Color(model.getActiveModel().getDefaultTint().asARGB());
- if (model.getActiveModel().isMarkedHurt()) color = new Color(model.getActiveModel().getDamageTint().asARGB());
-
- if (firstSend) {
- if (color.equals(lastColor)) return;
- }
-
- for (Player player : players) {
- EntityUtils.sendCustomColor(player, model.getEntity().getEntityId(), color);
- }
- }
-
- public void checkViewers(ModelEntityData model, Set viewers) {
+ public void checkViewers(EntityData model, Set viewers) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (!FloodgateApi.getInstance().isFloodgatePlayer(onlinePlayer.getUniqueId())) continue;
@@ -96,12 +57,12 @@ public class EntityTaskManager {
}
}
- private void sendSpawnPacket(ModelEntityData model, Player onlinePlayer) {
- EntityTaskRunnable task = model.getEntityTask();
+ private void sendSpawnPacket(EntityData model, Player onlinePlayer) {
+ TaskHandler task = model.getEntityTask();
boolean firstJoined = !plugin.getModelManager().getPlayerJoinedCache().contains(onlinePlayer.getUniqueId());
if (firstJoined) {
- task.sendEntityData(model, onlinePlayer, plugin.getConfigManager().getConfig().getInt("join-send-delay") / 50);
+ task.sendEntityData(model, onlinePlayer, plugin.getConfigManager().getConfig().getInt("models.join-send-delay") / 50);
} else {
task.sendEntityData(model, onlinePlayer, 5);
}
@@ -122,31 +83,20 @@ public class EntityTaskManager {
return true;
}
- public void sendHitBoxToAll(ModelEntityData model) {
+ public void sendHitBoxToAll(EntityData model) {
for (Player viewer : model.getViewers()) {
EntityUtils.sendCustomHitBox(viewer, model.getEntity().getEntityId(), 0.01f, 0.01f);
}
}
- public void sendHitBox(ModelEntityData model, Player viewer) {
- float w = 0;
-
- if (model.getActiveModel().isShadowVisible()) {
- if (model.getActiveModel().getModelRenderer() instanceof DisplayRenderer displayRenderer) {
- // w = displayRenderer.getHitbox().getShadowRadius().get();
- }
- }
-
- EntityUtils.sendCustomHitBox(viewer, model.getEntity().getEntityId(), 0.02f, w);
- }
-
- public boolean hasAnimation(ModelEntityData model, String animation) {
+ //TODO move this
+ public boolean hasAnimation(ModelEngineEntityData model, String animation) {
ActiveModel activeModel = model.getActiveModel();
BlueprintAnimation animationProperty = activeModel.getBlueprint().getAnimations().get(animation);
return !(animationProperty == null);
}
- public Method getScaleMethod() {
- return scaleMethod;
+ public PropertyHandler getPropertyHandler() {
+ return propertyHandler;
}
}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/ModelManager.java b/src/main/java/re/imc/geysermodelengine/managers/model/ModelManager.java
index 37eaa14..1f21d23 100644
--- a/src/main/java/re/imc/geysermodelengine/managers/model/ModelManager.java
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/ModelManager.java
@@ -1,13 +1,15 @@
package re.imc.geysermodelengine.managers.model;
-import com.ticxo.modelengine.api.ModelEngineAPI;
import com.ticxo.modelengine.api.model.ActiveModel;
-import com.ticxo.modelengine.api.model.ModeledEntity;
import com.ticxo.modelengine.api.model.bone.type.Mount;
import org.apache.commons.lang3.tuple.Pair;
-import org.bukkit.entity.Entity;
+import org.bukkit.Bukkit;
import re.imc.geysermodelengine.GeyserModelEngine;
-import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.managers.model.model.Model;
+import re.imc.geysermodelengine.managers.model.modelhandler.BetterModelHandler;
+import re.imc.geysermodelengine.managers.model.modelhandler.ModelEngineHandler;
+import re.imc.geysermodelengine.managers.model.modelhandler.ModelHandler;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -16,57 +18,53 @@ public class ModelManager {
private final GeyserModelEngine plugin;
+ private ModelHandler modelHandler;
+
private final HashSet playerJoinedCache = new HashSet<>();
- private final ConcurrentHashMap> entitiesCache = new ConcurrentHashMap<>();
- private final ConcurrentHashMap modelEntitiesCache = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap modelEntitiesCache = new ConcurrentHashMap<>();
+ private final ConcurrentHashMap> entitiesCache = new ConcurrentHashMap<>();
+ // MEG ONLY
private final ConcurrentHashMap> driversCache = new ConcurrentHashMap<>();
public ModelManager(GeyserModelEngine plugin) {
this.plugin = plugin;
- }
- public void create(ModeledEntity entity, ActiveModel model) {
- ModelEntityData modelEntity = new ModelEntityData(plugin, entity, model);
- int id = entity.getBase().getEntityId();
-
- Map map = entitiesCache.computeIfAbsent(id, k -> new HashMap<>());
-
- for (Map.Entry entry : map.entrySet()) {
- if (entry.getKey() != model && entry.getKey().getBlueprint().getName().equals(model.getBlueprint().getName())) {
- return;
- }
+ if (Bukkit.getPluginManager().getPlugin("ModelEngine") != null) {
+ this.modelHandler = new ModelEngineHandler(plugin);
+ plugin.getLogger().info("Using ModelEngine handler!");
+ } else if (Bukkit.getPluginManager().getPlugin("BetterModel") != null) {
+ this.modelHandler = new BetterModelHandler(plugin);
+ plugin.getLogger().info("Using BetterModel handler!");
+ } else {
+ plugin.getLogger().severe("No supported model engine found!");
+ plugin.getServer().getPluginManager().disablePlugin(plugin);
+ return;
}
- map.put(model, modelEntity);
- }
-
- public void processEntities(Entity entity) {
- if (entitiesCache.containsKey(entity.getEntityId())) return;
-
- ModeledEntity modeledEntity = ModelEngineAPI.getModeledEntity(entity);
- if (modeledEntity == null) return;
-
- Optional model = modeledEntity.getModels().values().stream().findFirst();
- model.ifPresent(m -> create(modeledEntity, m));
+ modelHandler.loadListeners();
}
public void removeEntities() {
- for (Map entities : entitiesCache.values()) {
+ for (Map entities : entitiesCache.values()) {
entities.forEach((model, modelEntity) -> modelEntity.getEntity().remove());
}
}
+ public ModelHandler getModelHandler() {
+ return modelHandler;
+ }
+
public HashSet getPlayerJoinedCache() {
return playerJoinedCache;
}
- public ConcurrentHashMap> getEntitiesCache() {
+ public ConcurrentHashMap> getEntitiesCache() {
return entitiesCache;
}
- public ConcurrentHashMap getModelEntitiesCache() {
+ public ConcurrentHashMap getModelEntitiesCache() {
return modelEntitiesCache;
}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/entity/BetterModelEntityData.java b/src/main/java/re/imc/geysermodelengine/managers/model/entity/BetterModelEntityData.java
new file mode 100644
index 0000000..0b358fb
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/entity/BetterModelEntityData.java
@@ -0,0 +1,86 @@
+package re.imc.geysermodelengine.managers.model.entity;
+
+import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
+import com.google.common.collect.Sets;
+import kr.toxicity.model.api.tracker.EntityTracker;
+import kr.toxicity.model.api.tracker.Tracker;
+import org.bukkit.Location;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import re.imc.geysermodelengine.GeyserModelEngine;
+import re.imc.geysermodelengine.managers.model.taskshandler.BetterModelTaskHandler;
+import re.imc.geysermodelengine.packet.entity.PacketEntity;
+
+import java.util.Set;
+
+public class BetterModelEntityData implements EntityData {
+
+ private final GeyserModelEngine plugin;
+
+ private final PacketEntity entity;
+ private final Set viewers = Sets.newConcurrentHashSet();
+
+ private final Entity entitySource;
+ private final Tracker tracker;
+ private final EntityTracker entityTracker;
+
+ private BetterModelTaskHandler entityTask;
+
+ private boolean hurt;
+
+ public BetterModelEntityData(GeyserModelEngine plugin, Entity entitySource, Tracker tracker, EntityTracker entityTracker) {
+ this.plugin = plugin;
+
+ this.entitySource = entitySource;
+ this.tracker = tracker;
+ this.entityTracker = entityTracker;
+ this.entity = new PacketEntity(EntityTypes.PIG, viewers, entitySource.getLocation());
+
+ runEntityTask();
+ }
+
+ @Override
+ public void teleportToModel() {
+ Location location = entitySource.getLocation();
+ entity.teleport(location);
+ }
+
+ public void runEntityTask() {
+ entityTask = new BetterModelTaskHandler(plugin, this);
+ }
+
+ @Override
+ public PacketEntity getEntity() {
+ return entity;
+ }
+
+ @Override
+ public Set getViewers() {
+ return viewers;
+ }
+
+ @Override
+ public BetterModelTaskHandler getEntityTask() {
+ return entityTask;
+ }
+
+ public void setHurt(boolean hurt) {
+ this.hurt = hurt;
+ }
+
+ public Entity getEntitySource() {
+ return entitySource;
+ }
+
+ public Tracker getTracker() {
+ return tracker;
+ }
+
+ public EntityTracker getEntityTracker() {
+ return entityTracker;
+ }
+
+ public boolean isHurt() {
+ return hurt;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/entity/EntityData.java b/src/main/java/re/imc/geysermodelengine/managers/model/entity/EntityData.java
new file mode 100644
index 0000000..780dd41
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/entity/EntityData.java
@@ -0,0 +1,30 @@
+package re.imc.geysermodelengine.managers.model.entity;
+
+import org.bukkit.entity.Player;
+import re.imc.geysermodelengine.managers.model.taskshandler.TaskHandler;
+import re.imc.geysermodelengine.packet.entity.PacketEntity;
+
+import java.util.Set;
+
+public interface EntityData {
+
+ /**
+ * Teleports the packet entity to the model
+ */
+ void teleportToModel();
+
+ /**
+ * Gets the packet Entity
+ */
+ PacketEntity getEntity();
+
+ /**
+ * Gets the entity view of players
+ */
+ Set getViewers();
+
+ /**
+ * Get the entity task handler
+ */
+ TaskHandler getEntityTask();
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/data/ModelEntityData.java b/src/main/java/re/imc/geysermodelengine/managers/model/entity/ModelEngineEntityData.java
similarity index 63%
rename from src/main/java/re/imc/geysermodelengine/managers/model/data/ModelEntityData.java
rename to src/main/java/re/imc/geysermodelengine/managers/model/entity/ModelEngineEntityData.java
index eadfd1b..4454775 100644
--- a/src/main/java/re/imc/geysermodelengine/managers/model/data/ModelEntityData.java
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/entity/ModelEngineEntityData.java
@@ -1,4 +1,4 @@
-package re.imc.geysermodelengine.managers.model.data;
+package re.imc.geysermodelengine.managers.model.entity;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.google.common.collect.Sets;
@@ -7,57 +7,58 @@ import com.ticxo.modelengine.api.model.ModeledEntity;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.GeyserModelEngine;
+import re.imc.geysermodelengine.managers.model.taskshandler.ModelEngineTaskHandler;
import re.imc.geysermodelengine.packet.entity.PacketEntity;
-import re.imc.geysermodelengine.runnables.EntityTaskRunnable;
import java.util.Set;
-public class ModelEntityData {
+public class ModelEngineEntityData implements EntityData {
private final GeyserModelEngine plugin;
- private PacketEntity entity;
-
+ private final PacketEntity entity;
private final Set viewers = Sets.newConcurrentHashSet();
private final ModeledEntity modeledEntity;
-
private final ActiveModel activeModel;
- private EntityTaskRunnable entityTask;
+ private ModelEngineTaskHandler entityTask;
- public ModelEntityData(GeyserModelEngine plugin, ModeledEntity modeledEntity, ActiveModel model) {
+ public ModelEngineEntityData(GeyserModelEngine plugin, ModeledEntity modeledEntity, ActiveModel activeModel) {
this.plugin = plugin;
this.modeledEntity = modeledEntity;
- this.activeModel = model;
- this.entity = spawnEntity();
+ this.activeModel = activeModel;
+ this.entity = new PacketEntity(EntityTypes.PIG, viewers, modeledEntity.getBase().getLocation());
runEntityTask();
}
+ @Override
public void teleportToModel() {
Location location = modeledEntity.getBase().getLocation();
entity.teleport(location);
}
- public PacketEntity spawnEntity() {
- entity = new PacketEntity(EntityTypes.PIG, viewers, modeledEntity.getBase().getLocation());
- return entity;
- }
-
public void runEntityTask() {
- entityTask = new EntityTaskRunnable(plugin, this);
+ entityTask = new ModelEngineTaskHandler(plugin, this);
}
+ @Override
public PacketEntity getEntity() {
return entity;
}
+ @Override
public Set getViewers() {
return viewers;
}
+ @Override
+ public ModelEngineTaskHandler getEntityTask() {
+ return entityTask;
+ }
+
public ModeledEntity getModeledEntity() {
return modeledEntity;
}
@@ -65,8 +66,4 @@ public class ModelEntityData {
public ActiveModel getActiveModel() {
return activeModel;
}
-
- public EntityTaskRunnable getEntityTask() {
- return entityTask;
- }
}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/model/BetterModelModel.java b/src/main/java/re/imc/geysermodelengine/managers/model/model/BetterModelModel.java
new file mode 100644
index 0000000..b8f83c5
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/model/BetterModelModel.java
@@ -0,0 +1,41 @@
+package re.imc.geysermodelengine.managers.model.model;
+
+import kr.toxicity.model.api.tracker.Tracker;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.managers.model.modelhandler.ModelHandler;
+import re.imc.geysermodelengine.managers.model.propertyhandler.PropertyHandler;
+
+public class BetterModelModel implements Model {
+
+ private final Tracker tracker;
+ private final ModelHandler modelHandler;
+ private final EntityData entityData;
+ private final PropertyHandler propertyHandler;
+
+ public BetterModelModel(Tracker tracker, ModelHandler modelHandler, EntityData entityData, PropertyHandler propertyHandler) {
+ this.tracker = tracker;
+ this.modelHandler = modelHandler;
+ this.entityData = entityData;
+ this.propertyHandler = propertyHandler;
+ }
+
+ @Override
+ public String getName() {
+ return tracker.name();
+ }
+
+ @Override
+ public ModelHandler getModelHandler() {
+ return modelHandler;
+ }
+
+ @Override
+ public EntityData getEntityData() {
+ return entityData;
+ }
+
+ @Override
+ public PropertyHandler getPropertyHandler() {
+ return propertyHandler;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/model/Model.java b/src/main/java/re/imc/geysermodelengine/managers/model/model/Model.java
new file mode 100644
index 0000000..44710a9
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/model/Model.java
@@ -0,0 +1,28 @@
+package re.imc.geysermodelengine.managers.model.model;
+
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.managers.model.modelhandler.ModelHandler;
+import re.imc.geysermodelengine.managers.model.propertyhandler.PropertyHandler;
+
+public interface Model {
+
+ /**
+ * Gets the model's name
+ */
+ String getName();
+
+ /**
+ * Gets the model's entity data
+ */
+ EntityData getEntityData();
+
+ /**
+ * Gets the model's model handler
+ */
+ ModelHandler getModelHandler();
+
+ /**
+ * Gets the model's property handler
+ */
+ PropertyHandler getPropertyHandler();
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/model/ModelEngineModel.java b/src/main/java/re/imc/geysermodelengine/managers/model/model/ModelEngineModel.java
new file mode 100644
index 0000000..edf32ee
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/model/ModelEngineModel.java
@@ -0,0 +1,45 @@
+package re.imc.geysermodelengine.managers.model.model;
+
+import com.ticxo.modelengine.api.model.ActiveModel;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.managers.model.modelhandler.ModelHandler;
+import re.imc.geysermodelengine.managers.model.propertyhandler.PropertyHandler;
+
+public class ModelEngineModel implements Model {
+
+ private final ActiveModel activeModel;
+ private final ModelHandler modelHandler;
+ private final EntityData entityData;
+ private final PropertyHandler propertyHandler;
+
+ public ModelEngineModel(ActiveModel activeModel, ModelHandler modelHandler, EntityData entityData, PropertyHandler propertyHandler) {
+ this.activeModel = activeModel;
+ this.modelHandler = modelHandler;
+ this.entityData = entityData;
+ this.propertyHandler = propertyHandler;
+ }
+
+ @Override
+ public String getName() {
+ return activeModel.getBlueprint().getName();
+ }
+
+ @Override
+ public ModelHandler getModelHandler() {
+ return modelHandler;
+ }
+
+ @Override
+ public EntityData getEntityData() {
+ return entityData;
+ }
+
+ @Override
+ public PropertyHandler getPropertyHandler() {
+ return propertyHandler;
+ }
+
+ public ActiveModel getActiveModel() {
+ return activeModel;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/BetterModelHandler.java b/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/BetterModelHandler.java
new file mode 100644
index 0000000..b71fd3d
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/BetterModelHandler.java
@@ -0,0 +1,65 @@
+package re.imc.geysermodelengine.managers.model.modelhandler;
+
+import kr.toxicity.model.api.tracker.EntityTracker;
+import kr.toxicity.model.api.tracker.Tracker;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Entity;
+import re.imc.geysermodelengine.GeyserModelEngine;
+import re.imc.geysermodelengine.listener.BetterModelListener;
+import re.imc.geysermodelengine.managers.model.propertyhandler.PropertyHandler;
+import re.imc.geysermodelengine.managers.model.entity.BetterModelEntityData;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.managers.model.model.BetterModelModel;
+import re.imc.geysermodelengine.managers.model.model.Model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class BetterModelHandler implements ModelHandler {
+
+ private final GeyserModelEngine plugin;
+
+ public BetterModelHandler(GeyserModelEngine plugin) {
+ this.plugin = plugin;
+ }
+
+ //TODO fix dupe issue - dupe happens when server restart
+ @Override
+ public void createModel(Object... objects) {
+ Entity entitySource = (Entity) objects[0];
+ Tracker tracker = (Tracker) objects[1];
+ EntityTracker entityTracker = (EntityTracker) objects[2];
+
+ int entityID = entitySource.getEntityId();
+
+ PropertyHandler propertyHandler = plugin.getEntityTaskManager().getPropertyHandler();
+ EntityData entityData = new BetterModelEntityData(plugin, entitySource, tracker, entityTracker);
+
+ Model model = new BetterModelModel(tracker, this, entityData, propertyHandler);
+
+ Map entityDataCache = plugin.getModelManager().getEntitiesCache().computeIfAbsent(entityID, k -> new HashMap<>());
+
+ for (Map.Entry entry : entityDataCache.entrySet()) {
+ if (entry.getKey() != model && entry.getKey().getName().equals(tracker.name())) {
+ return;
+ }
+ }
+
+ plugin.getModelManager().getModelEntitiesCache().put(entityID, model);
+ entityDataCache.put(model, entityData);
+ }
+
+ @Override
+ public void processEntities(Entity entity) {
+// if (plugin.getModelManager().getEntitiesCache().containsKey(entity.getEntityId())) return;
+//
+// @NotNull Optional modeledEntity = BetterModel.registry(entity);
+//
+// modeledEntity.ifPresent(m -> createModel(modeledEntity.get().entity(), m.));
+ }
+
+ @Override
+ public void loadListeners() {
+ Bukkit.getPluginManager().registerEvents(new BetterModelListener(plugin), plugin);
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelEngineHandler.java b/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelEngineHandler.java
new file mode 100644
index 0000000..e2aea3a
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelEngineHandler.java
@@ -0,0 +1,69 @@
+package re.imc.geysermodelengine.managers.model.modelhandler;
+
+import com.ticxo.modelengine.api.ModelEngineAPI;
+import com.ticxo.modelengine.api.model.ActiveModel;
+import com.ticxo.modelengine.api.model.ModeledEntity;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Entity;
+import re.imc.geysermodelengine.GeyserModelEngine;
+import re.imc.geysermodelengine.listener.ModelEngineListener;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.managers.model.entity.ModelEngineEntityData;
+import re.imc.geysermodelengine.managers.model.model.Model;
+import re.imc.geysermodelengine.managers.model.model.ModelEngineModel;
+import re.imc.geysermodelengine.managers.model.propertyhandler.PropertyHandler;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+
+public class ModelEngineHandler implements ModelHandler {
+
+ //TODO move driver hashmap here
+
+ private final GeyserModelEngine plugin;
+
+ public ModelEngineHandler(GeyserModelEngine plugin) {
+ this.plugin = plugin;
+ }
+
+ @Override
+ public void createModel(Object... objects) {
+ ModeledEntity megEntity = (ModeledEntity) objects[0];
+ ActiveModel megActiveModel = (ActiveModel) objects[1];
+
+ int entityID = megEntity.getBase().getEntityId();
+
+ PropertyHandler propertyHandler = plugin.getEntityTaskManager().getPropertyHandler();
+ EntityData entityData = new ModelEngineEntityData(plugin, megEntity, megActiveModel);
+
+ Model model = new ModelEngineModel(megActiveModel, this, entityData, propertyHandler);
+
+ Map entityDataCache = plugin.getModelManager().getEntitiesCache().computeIfAbsent(entityID, k -> new HashMap<>());
+
+ for (Map.Entry entry : entityDataCache.entrySet()) {
+ if (entry.getKey() != model && entry.getKey().getName().equals(megActiveModel.getBlueprint().getName())) {
+ return;
+ }
+ }
+
+ plugin.getModelManager().getModelEntitiesCache().put(entityID, model);
+ entityDataCache.put(model, entityData);
+ }
+
+ @Override
+ public void processEntities(Entity entity) {
+ if (plugin.getModelManager().getEntitiesCache().containsKey(entity.getEntityId())) return;
+
+ ModeledEntity modeledEntity = ModelEngineAPI.getModeledEntity(entity);
+ if (modeledEntity == null) return;
+
+ Optional model = modeledEntity.getModels().values().stream().findFirst();
+ model.ifPresent(m -> createModel(modeledEntity, m));
+ }
+
+ @Override
+ public void loadListeners() {
+ Bukkit.getPluginManager().registerEvents(new ModelEngineListener(plugin), plugin);
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelHandler.java b/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelHandler.java
new file mode 100644
index 0000000..30801af
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/modelhandler/ModelHandler.java
@@ -0,0 +1,23 @@
+package re.imc.geysermodelengine.managers.model.modelhandler;
+
+import org.bukkit.entity.Entity;
+
+public interface ModelHandler {
+
+ /**
+ * Creates the model from the required Model Engine
+ * @param objects Processes the required objects
+ */
+ void createModel(Object... objects);
+
+ /**
+ * Processes entities into createModel()
+ * @param entity Registers bukkit entities
+ */
+ void processEntities(Entity entity);
+
+ /**
+ * Loads the required listeners
+ */
+ void loadListeners();
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/BetterModelPropertyHandler.java b/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/BetterModelPropertyHandler.java
new file mode 100644
index 0000000..2e46acf
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/BetterModelPropertyHandler.java
@@ -0,0 +1,169 @@
+package re.imc.geysermodelengine.managers.model.propertyhandler;
+
+import kr.toxicity.model.api.animation.AnimationIterator;
+import kr.toxicity.model.api.bone.RenderedBone;
+import kr.toxicity.model.api.data.blueprint.BlueprintAnimation;
+import kr.toxicity.model.api.data.renderer.RenderPipeline;
+import kr.toxicity.model.api.nms.ModelDisplay;
+import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import re.imc.geysermodelengine.GeyserModelEngine;
+import re.imc.geysermodelengine.managers.model.entity.BetterModelEntityData;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.util.BooleanPacker;
+
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+public class BetterModelPropertyHandler implements PropertyHandler {
+
+ private final GeyserModelEngine plugin;
+
+ public BetterModelPropertyHandler(GeyserModelEngine plugin) {
+ this.plugin = plugin;
+ }
+
+ // Figure out on how to get the scale from BetterModel
+ @Override
+ public void sendScale(EntityData entityData, Collection players, float lastScale, boolean firstSend) {
+ BetterModelEntityData betterModelEntityData = (BetterModelEntityData) entityData;
+ }
+
+ @Override
+ public void sendColor(EntityData entityData, Collection players, Color lastColor, boolean firstSend) {
+ if (players.isEmpty()) return;
+
+ 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;
+ }
+
+ for (Player player : players) {
+ EntityUtils.sendCustomColor(player, betterModelEntityData.getEntity().getEntityId(), color);
+ }
+
+ betterModelEntityData.setHurt(false);
+ }
+
+ @Override
+ public void sendHitBox(EntityData entityData, Player player) {
+ BetterModelEntityData betterModelEntityData = (BetterModelEntityData) entityData;
+
+ float w = 0;
+
+ EntityUtils.sendCustomHitBox(player, betterModelEntityData.getEntity().getEntityId(), 0.02f, w);
+ }
+
+ @Override
+ public void updateEntityProperties(EntityData entityData, Collection players, boolean firstSend, String... forceAnims) {
+ BetterModelEntityData model = (BetterModelEntityData) entityData;
+
+ int entity = model.getEntity().getEntityId();
+ Set forceAnimSet = Set.of(forceAnims);
+
+ Map boneUpdates = new HashMap<>();
+ Map animUpdates = new HashMap<>();
+ Set anims = new HashSet<>();
+
+ model.getTracker().bones().forEach(bone -> processBone(model, bone, boneUpdates));
+
+ RenderPipeline handler = model.getTracker().getPipeline();
+
+ for (RenderedBone renderedBone : handler.bones()) {
+ if (model.getTracker().bone(renderedBone.name()).runningAnimation() != null) {
+ BlueprintAnimation anim = model.getTracker().renderer().animations().get(renderedBone.runningAnimation().name());
+
+ anims.add(renderedBone.runningAnimation().name());
+ if (anim.override() && anim.loop() == AnimationIterator.Type.PLAY_ONCE) {
+ break;
+ }
+ }
+ }
+
+ for (String id : handler.getParent().animations().keySet()) {
+ if (anims.contains(id)) {
+ animUpdates.put(id, true);
+ } else {
+ animUpdates.put(id, false);
+ }
+ }
+
+ Set lastPlayed = new HashSet<>(model.getEntityTask().getLastPlayedAnim().asMap().keySet());
+
+ for (Map.Entry anim : animUpdates.entrySet()) {
+ if (anim.getValue()) {
+ model.getEntityTask().getLastPlayedAnim().put(anim.getKey(), true);
+ }
+ }
+
+ for (String anim : lastPlayed) animUpdates.put(anim, true);
+
+ if (boneUpdates.isEmpty() && animUpdates.isEmpty()) return;
+
+ Map intUpdates = new HashMap<>();
+ int i = 0;
+
+ for (Integer integer : BooleanPacker.mapBooleansToInts(boneUpdates)) {
+ intUpdates.put(plugin.getConfigManager().getConfig().getString("models.namespace") + ":bone" + i, integer);
+ i++;
+ }
+
+ i = 0;
+ for (Integer integer : BooleanPacker.mapBooleansToInts(animUpdates)) {
+ intUpdates.put(plugin.getConfigManager().getConfig().getString("models.namespace") + ":anim" + i, integer);
+ i++;
+ }
+
+ if (!firstSend) {
+ if (intUpdates.equals(model.getEntityTask().getLastIntSet())) {
+ return;
+ } else {
+ model.getEntityTask().getLastIntSet().clear();
+ model.getEntityTask().getLastIntSet().putAll(intUpdates);
+ }
+ }
+
+ if (plugin.getConfigManager().getConfig().getBoolean("options.debug")) plugin.getLogger().info(animUpdates.toString());
+
+ List list = new ArrayList<>(boneUpdates.keySet());
+ Collections.sort(list);
+
+ for (Player player : players) {
+ EntityUtils.sendIntProperties(player, entity, intUpdates);
+ }
+ }
+
+ public String unstripName(RenderedBone bone) {
+ @NotNull String name = bone.name().rawName();
+
+ if (name.equals("head")) {
+ if (!bone.getChildren().isEmpty()) return "hi_" + name;
+ return "h_" + name;
+ }
+
+ return name;
+ }
+
+ private void processBone(BetterModelEntityData entityData, RenderedBone bone, Map map) {
+ String name = unstripName(bone).toLowerCase();
+ if (name.equals("hitbox") || name.equals("shadow") || name.equals("mount") || name.startsWith("p_") || name.startsWith("b_") || name.startsWith("ob_")) return;
+
+ for (RenderedBone renderedBone : bone.getChildren().values()) {
+ processBone(entityData, renderedBone, map);
+ }
+
+ RenderedBone activeBone = entityData.getTracker().bone(bone.name());
+
+ ModelDisplay modelDisplay = activeBone.getDisplay();
+ if (modelDisplay == null) return;
+ boolean visible = activeBone.getDisplay().invisible();
+
+ map.put(name, visible);
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/ModelEnginePropertyHandler.java b/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/ModelEnginePropertyHandler.java
new file mode 100644
index 0000000..b9dd73d
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/ModelEnginePropertyHandler.java
@@ -0,0 +1,186 @@
+package re.imc.geysermodelengine.managers.model.propertyhandler;
+
+import com.ticxo.modelengine.api.animation.BlueprintAnimation;
+import com.ticxo.modelengine.api.animation.handler.AnimationHandler;
+import com.ticxo.modelengine.api.generator.blueprint.BlueprintBone;
+import com.ticxo.modelengine.api.model.bone.ModelBone;
+import com.ticxo.modelengine.api.model.render.DisplayRenderer;
+import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
+import org.bukkit.entity.Player;
+import org.joml.Vector3fc;
+import re.imc.geysermodelengine.GeyserModelEngine;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.managers.model.entity.ModelEngineEntityData;
+import re.imc.geysermodelengine.util.BooleanPacker;
+
+import java.awt.*;
+import java.util.*;
+import java.util.List;
+
+public class ModelEnginePropertyHandler implements PropertyHandler {
+
+ private final GeyserModelEngine plugin;
+
+ public ModelEnginePropertyHandler(GeyserModelEngine plugin) {
+ this.plugin = plugin;
+ }
+
+ @Override
+ public void sendScale(EntityData modelData, Collection players, float lastScale, boolean firstSend) {
+ try {
+ if (players.isEmpty()) return;
+
+ ModelEngineEntityData modelEngineEntityData = (ModelEngineEntityData) modelData;
+
+ Vector3fc scale = modelEngineEntityData.getActiveModel().getScale();
+
+ float average = (scale.x() + scale.y() + scale.z()) / 3;
+
+ if (!firstSend) {
+ if (average == lastScale) return;
+ }
+
+ for (Player player : players) {
+ EntityUtils.sendCustomScale(player, modelEngineEntityData.getEntity().getEntityId(), average);
+ }
+ } catch (Throwable ignored) {}
+ }
+
+ @Override
+ public void sendColor(EntityData entityData, Collection players, Color lastColor, boolean firstSend) {
+ if (players.isEmpty()) return;
+
+ ModelEngineEntityData modelEngineEntityData = (ModelEngineEntityData) entityData;
+
+ Color color = new Color(modelEngineEntityData.getActiveModel().getDefaultTint().asARGB());
+ if (modelEngineEntityData.getActiveModel().isMarkedHurt()) color = new Color(modelEngineEntityData.getActiveModel().getDamageTint().asARGB());
+
+ if (firstSend) {
+ if (color.equals(lastColor)) return;
+ }
+
+ for (Player player : players) {
+ EntityUtils.sendCustomColor(player, modelEngineEntityData.getEntity().getEntityId(), color);
+ }
+ }
+
+ @Override
+ public void sendHitBox(EntityData entityData, Player player) {
+ ModelEngineEntityData modelEngineEntityData = (ModelEngineEntityData) entityData;
+
+ float w = 0;
+
+ if (modelEngineEntityData.getActiveModel().isShadowVisible()) {
+ if (modelEngineEntityData.getActiveModel().getModelRenderer() instanceof DisplayRenderer displayRenderer) {
+ // w = displayRenderer.getHitbox().getShadowRadius().get();
+ }
+ }
+
+ EntityUtils.sendCustomHitBox(player, modelEngineEntityData.getEntity().getEntityId(), 0.02f, w);
+ }
+
+ @Override
+ public void updateEntityProperties(EntityData entityData, Collection players, boolean firstSend, String... forceAnims) {
+ ModelEngineEntityData model = (ModelEngineEntityData) entityData;
+
+ int entity = model.getEntity().getEntityId();
+ Set forceAnimSet = Set.of(forceAnims);
+
+ Map boneUpdates = new HashMap<>();
+ Map animUpdates = new HashMap<>();
+ Set anims = new HashSet<>();
+
+ model.getActiveModel().getBlueprint().getBones().forEach((s, bone) -> processBone(model, bone, boneUpdates));
+
+ AnimationHandler handler = model.getActiveModel().getAnimationHandler();
+ Set priority = model.getActiveModel().getBlueprint().getAnimationDescendingPriority();
+ for (String animId : priority) {
+ if (handler.isPlayingAnimation(animId)) {
+ BlueprintAnimation anim = model.getActiveModel().getBlueprint().getAnimations().get(animId);
+
+ anims.add(animId);
+ if (anim.isOverride() && anim.getLoopMode() == BlueprintAnimation.LoopMode.ONCE) {
+ break;
+ }
+ }
+ }
+
+ for (String id : priority) {
+ if (anims.contains(id)) {
+ animUpdates.put(id, true);
+ } else {
+ animUpdates.put(id, false);
+ }
+ }
+
+ Set lastPlayed = new HashSet<>(model.getEntityTask().getLastPlayedAnim().asMap().keySet());
+
+ for (Map.Entry anim : animUpdates.entrySet()) {
+ if (anim.getValue()) {
+ model.getEntityTask().getLastPlayedAnim().put(anim.getKey(), true);
+ }
+ }
+
+ for (String anim : lastPlayed) animUpdates.put(anim, true);
+
+ if (boneUpdates.isEmpty() && animUpdates.isEmpty()) return;
+
+ Map intUpdates = new HashMap<>();
+ int i = 0;
+
+ for (Integer integer : BooleanPacker.mapBooleansToInts(boneUpdates)) {
+ intUpdates.put(plugin.getConfigManager().getConfig().getString("models.namespace") + ":bone" + i, integer);
+ i++;
+ }
+
+ i = 0;
+ for (Integer integer : BooleanPacker.mapBooleansToInts(animUpdates)) {
+ intUpdates.put(plugin.getConfigManager().getConfig().getString("models.namespace") + ":anim" + i, integer);
+ i++;
+ }
+
+ if (!firstSend) {
+ if (intUpdates.equals(model.getEntityTask().getLastIntSet())) {
+ return;
+ } else {
+ model.getEntityTask().getLastIntSet().clear();
+ model.getEntityTask().getLastIntSet().putAll(intUpdates);
+ }
+ }
+
+ if (plugin.getConfigManager().getConfig().getBoolean("options.debug")) plugin.getLogger().info(animUpdates.toString());
+
+ List list = new ArrayList<>(boneUpdates.keySet());
+ Collections.sort(list);
+
+ for (Player player : players) {
+ EntityUtils.sendIntProperties(player, entity, intUpdates);
+ }
+ }
+
+ public String unstripName(BlueprintBone bone) {
+ String name = bone.getName();
+ if (bone.getBehaviors().get("head") != null) {
+ if (!bone.getBehaviors().get("head").isEmpty()) return "hi_" + name;
+ return "h_" + name;
+ }
+
+ return name;
+ }
+
+ private void processBone(ModelEngineEntityData model, BlueprintBone bone, Map map) {
+ String name = unstripName(bone).toLowerCase();
+ if (name.equals("hitbox") || name.equals("shadow") || name.equals("mount") || name.startsWith("p_") || name.startsWith("b_") || name.startsWith("ob_")) return;
+
+ for (BlueprintBone blueprintBone : bone.getChildren().values()) {
+ processBone(model, blueprintBone, map);
+ }
+
+ ModelBone activeBone = model.getActiveModel().getBones().get(bone.getName());
+
+ boolean visible = false;
+ if (activeBone != null) visible = activeBone.isVisible();
+
+ map.put(name, visible);
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/PropertyHandler.java b/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/PropertyHandler.java
new file mode 100644
index 0000000..793afcf
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/propertyhandler/PropertyHandler.java
@@ -0,0 +1,44 @@
+package re.imc.geysermodelengine.managers.model.propertyhandler;
+
+import org.bukkit.entity.Player;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+
+import java.awt.*;
+import java.util.Collection;
+
+public interface PropertyHandler {
+
+ /**
+ * Sends scale of the entity to the player
+ * @param entityData The data of the entity
+ * @param players Collection of players from the entity view
+ * @param lastScale Sends the last scale to the player
+ * @param firstSend Checks if it's the first time to send scale to the player
+ */
+ void sendScale(EntityData entityData, Collection players, float lastScale, boolean firstSend);
+
+ /**
+ * Sends a colour tint to the player
+ * @param entityData The data of the entity
+ * @param players Collection of players from the entity view
+ * @param lastColor Sends the last colour to the player
+ * @param firstSend Checks if it's the first time to send colour to the player
+ */
+ void sendColor(EntityData entityData, Collection players, Color lastColor, boolean firstSend);
+
+ /**
+ * Sends a hitbox to the player
+ * @param entityData The data of the entity
+ * @param player Sends the player the entity hitbox
+ */
+ void sendHitBox(EntityData entityData, Player player);
+
+ /**
+ * Updates the entity to all viewable players
+ * @param entityData The data of the entity
+ * @param players Collection of players from the entity view
+ * @param firstSend Checks if it's the first time to send the entity to the player
+ * @param forceAnims Forces the entity to do an animation
+ */
+ void updateEntityProperties(EntityData entityData, Collection players, boolean firstSend, String... forceAnims);
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/BetterModelTaskHandler.java b/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/BetterModelTaskHandler.java
new file mode 100644
index 0000000..18c91fd
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/BetterModelTaskHandler.java
@@ -0,0 +1,170 @@
+package re.imc.geysermodelengine.managers.model.taskshandler;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import kr.toxicity.model.api.tracker.Tracker;
+import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import re.imc.geysermodelengine.GeyserModelEngine;
+import re.imc.geysermodelengine.managers.model.entity.BetterModelEntityData;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.packet.entity.PacketEntity;
+
+import java.awt.*;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+public class BetterModelTaskHandler implements TaskHandler {
+
+ private final GeyserModelEngine plugin;
+
+ private final BetterModelEntityData entityData;
+
+ private int tick = 0;
+ private int syncTick = 0;
+
+ private float lastScale = -1.0f;
+ private Color lastColor = null;
+
+ private boolean removed = false;
+
+ private final ConcurrentHashMap lastIntSet = new ConcurrentHashMap<>();
+ private final Cache lastPlayedAnim = CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MILLISECONDS).build();
+
+ private ScheduledFuture scheduledFuture;
+
+ public BetterModelTaskHandler(GeyserModelEngine plugin, BetterModelEntityData entityData) {
+ this.plugin = plugin;
+ this.entityData = entityData;
+
+ plugin.getEntityTaskManager().sendHitBoxToAll(entityData);
+ scheduledFuture = plugin.getSchedulerPool().scheduleAtFixedRate(this::runAsync, 0, 20, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void runAsync() {
+ plugin.getEntityTaskManager().checkViewers(entityData, entityData.getViewers());
+
+ PacketEntity entity = entityData.getEntity();
+ if (entity.isDead()) return;
+
+ Set viewers = entityData.getViewers();
+ Entity entitySource = entityData.getEntitySource();
+ Tracker tracker = entityData.getTracker();
+
+ entityData.teleportToModel();
+
+ if (entitySource.isDead() || tracker.forRemoval()) {
+ removed = true;
+ entity.remove();
+
+ plugin.getModelManager().getEntitiesCache().remove(entitySource.getEntityId());
+ plugin.getModelManager().getModelEntitiesCache().remove(entitySource.getEntityId());
+
+ cancel();
+ return;
+ }
+
+ if (tick % 5 == 0) {
+ if (tick % 40 == 0) {
+ for (Player viewer : Set.copyOf(viewers)) {
+ if (!plugin.getEntityTaskManager().canSee(viewer, entityData.getEntity())) {
+ viewers.remove(viewer);
+ }
+ }
+ }
+ }
+
+ tick++;
+ 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);
+ }
+
+ @Override
+ public void sendEntityData(EntityData entityData, Player player, int delay) {
+ BetterModelEntityData betterModelEntityData = (BetterModelEntityData) entityData;
+
+ EntityUtils.setCustomEntity(player, betterModelEntityData.getEntity().getEntityId(), plugin.getConfigManager().getConfig().getString("models.namespace") + ":" + betterModelEntityData.getTracker().name().toLowerCase());
+
+ plugin.getSchedulerPool().schedule(() -> {
+ entityData.getEntity().sendSpawnPacket(Collections.singletonList(player));
+
+ plugin.getSchedulerPool().schedule(() -> {
+ plugin.getEntityTaskManager().getPropertyHandler().sendHitBox(entityData, player);
+
+ plugin.getEntityTaskManager().getPropertyHandler().sendScale(entityData, Collections.singleton(player), lastScale, true);
+ plugin.getEntityTaskManager().getPropertyHandler().sendColor(entityData, Collections.singleton(player), lastColor, true);
+
+ plugin.getEntityTaskManager().getPropertyHandler().updateEntityProperties(entityData, Collections.singleton(player), true);
+ }, 500, TimeUnit.MILLISECONDS);
+ }, delay * 50L, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void cancel() {
+ scheduledFuture.cancel(true);
+ }
+
+ public void setTick(int tick) {
+ this.tick = tick;
+ }
+
+ public void setSyncTick(int syncTick) {
+ this.syncTick = syncTick;
+ }
+
+ public void setRemoved(boolean removed) {
+ this.removed = removed;
+ }
+
+ public void setLastScale(float lastScale) {
+ this.lastScale = lastScale;
+ }
+
+ public int getTick() {
+ return tick;
+ }
+
+ public int getSyncTick() {
+ return syncTick;
+ }
+
+ public void setLastColor(Color lastColor) {
+ this.lastColor = lastColor;
+ }
+
+ public float getLastScale() {
+ return lastScale;
+ }
+
+ public Color getLastColor() {
+ return lastColor;
+ }
+
+ public boolean isRemoved() {
+ return removed;
+ }
+
+ public ConcurrentHashMap getLastIntSet() {
+ return lastIntSet;
+ }
+
+ public Cache getLastPlayedAnim() {
+ return lastPlayedAnim;
+ }
+
+ public ScheduledFuture getScheduledFuture() {
+ return scheduledFuture;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/ModelEngineTaskHandler.java b/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/ModelEngineTaskHandler.java
new file mode 100644
index 0000000..dc11348
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/ModelEngineTaskHandler.java
@@ -0,0 +1,170 @@
+package re.imc.geysermodelengine.managers.model.taskshandler;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.ticxo.modelengine.api.model.ActiveModel;
+import com.ticxo.modelengine.api.model.ModeledEntity;
+import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
+import org.bukkit.entity.Player;
+import re.imc.geysermodelengine.GeyserModelEngine;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.managers.model.entity.ModelEngineEntityData;
+import re.imc.geysermodelengine.packet.entity.PacketEntity;
+
+import java.awt.*;
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+public class ModelEngineTaskHandler implements TaskHandler {
+
+ private final GeyserModelEngine plugin;
+
+ private final ModelEngineEntityData entityData;
+
+ private int tick = 0;
+ private int syncTick = 0;
+
+ private float lastScale = -1.0f;
+ private Color lastColor = null;
+
+ private boolean removed = false;
+
+ private final ConcurrentHashMap lastIntSet = new ConcurrentHashMap<>();
+ private final Cache lastPlayedAnim = CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MILLISECONDS).build();
+
+ private ScheduledFuture scheduledFuture;
+
+ public ModelEngineTaskHandler(GeyserModelEngine plugin, ModelEngineEntityData entityData) {
+ this.plugin = plugin;
+ this.entityData = entityData;
+
+ plugin.getEntityTaskManager().sendHitBoxToAll(entityData);
+ scheduledFuture = plugin.getSchedulerPool().scheduleAtFixedRate(this::runAsync, 0, 20, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void runAsync() {
+ plugin.getEntityTaskManager().checkViewers(entityData, entityData.getViewers());
+
+ PacketEntity entity = entityData.getEntity();
+ if (entity.isDead()) return;
+
+ entityData.teleportToModel();
+
+ Set viewers = entityData.getViewers();
+ ActiveModel activeModel = entityData.getActiveModel();
+ ModeledEntity modeledEntity = entityData.getModeledEntity();
+
+ if (activeModel.isDestroyed() || activeModel.isRemoved()) {
+ removed = true;
+ entity.remove();
+
+ plugin.getModelManager().getEntitiesCache().remove(modeledEntity.getBase().getEntityId());
+ plugin.getModelManager().getModelEntitiesCache().remove(modeledEntity.getBase().getEntityId());
+
+ cancel();
+ return;
+ }
+
+ if (tick % 5 == 0) {
+ if (tick % 40 == 0) {
+ for (Player viewer : Set.copyOf(viewers)) {
+ if (!plugin.getEntityTaskManager().canSee(viewer, entityData.getEntity())) {
+ viewers.remove(viewer);
+ }
+ }
+ }
+ }
+
+ tick ++;
+ 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);
+ }
+
+ @Override
+ public void sendEntityData(EntityData entityData, Player player, int delay) {
+ ModelEngineEntityData modelEngineEntityData = (ModelEngineEntityData) entityData;
+
+ EntityUtils.setCustomEntity(player, modelEngineEntityData.getEntity().getEntityId(), plugin.getConfigManager().getConfig().getString("models.namespace") + ":" + modelEngineEntityData.getActiveModel().getBlueprint().getName().toLowerCase());
+
+ plugin.getSchedulerPool().schedule(() -> {
+ entityData.getEntity().sendSpawnPacket(Collections.singletonList(player));
+
+ plugin.getSchedulerPool().schedule(() -> {
+ plugin.getEntityTaskManager().getPropertyHandler().sendHitBox(entityData, player);
+
+ plugin.getEntityTaskManager().getPropertyHandler().sendScale(entityData, Collections.singleton(player), lastScale, true);
+ plugin.getEntityTaskManager().getPropertyHandler().sendColor(entityData, Collections.singleton(player), lastColor, true);
+
+ plugin.getEntityTaskManager().getPropertyHandler().updateEntityProperties(entityData, Collections.singleton(player), true);
+ }, 500, TimeUnit.MILLISECONDS);
+ }, delay * 50L, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void cancel() {
+ scheduledFuture.cancel(true);
+ }
+
+ public void setTick(int tick) {
+ this.tick = tick;
+ }
+
+ public void setSyncTick(int syncTick) {
+ this.syncTick = syncTick;
+ }
+
+ public void setRemoved(boolean removed) {
+ this.removed = removed;
+ }
+
+ public void setLastScale(float lastScale) {
+ this.lastScale = lastScale;
+ }
+
+ public int getTick() {
+ return tick;
+ }
+
+ public int getSyncTick() {
+ return syncTick;
+ }
+
+ public void setLastColor(Color lastColor) {
+ this.lastColor = lastColor;
+ }
+
+ public float getLastScale() {
+ return lastScale;
+ }
+
+ public Color getLastColor() {
+ return lastColor;
+ }
+
+ public boolean isRemoved() {
+ return removed;
+ }
+
+ public ConcurrentHashMap getLastIntSet() {
+ return lastIntSet;
+ }
+
+ public Cache getLastPlayedAnim() {
+ return lastPlayedAnim;
+ }
+
+ public ScheduledFuture getScheduledFuture() {
+ return scheduledFuture;
+ }
+}
diff --git a/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/TaskHandler.java b/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/TaskHandler.java
new file mode 100644
index 0000000..f2af8a2
--- /dev/null
+++ b/src/main/java/re/imc/geysermodelengine/managers/model/taskshandler/TaskHandler.java
@@ -0,0 +1,25 @@
+package re.imc.geysermodelengine.managers.model.taskshandler;
+
+import org.bukkit.entity.Player;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+
+public interface TaskHandler {
+
+ /**
+ * Runs the entity scheduler
+ */
+ void runAsync();
+
+ /**
+ * Spawns the entity to the player
+ * @param entityData The data of the entity
+ * @param player Sends the entity to the player
+ * @param delay Delays sending the entity to the player
+ */
+ void sendEntityData(EntityData entityData, Player player, int delay);
+
+ /**
+ * Cancels the entity scheduler
+ */
+ void cancel();
+}
diff --git a/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java b/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java
index a74ffb1..db4592e 100644
--- a/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java
+++ b/src/main/java/re/imc/geysermodelengine/packet/entity/PacketEntity.java
@@ -58,7 +58,6 @@ public class PacketEntity {
return true;
}
-
public void remove() {
removed = true;
sendEntityDestroyPacket(viewers);
diff --git a/src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java b/src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java
index 37ba6ed..c2f163d 100644
--- a/src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java
+++ b/src/main/java/re/imc/geysermodelengine/runnables/BedrockMountControlRunnable.java
@@ -9,9 +9,9 @@ import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
-import org.geysermc.floodgate.api.FloodgateApi;
import re.imc.geysermodelengine.GeyserModelEngine;
+import java.util.UUID;
import java.util.function.Consumer;
public class BedrockMountControlRunnable implements Consumer {
@@ -24,8 +24,8 @@ public class BedrockMountControlRunnable implements Consumer {
@Override
public void accept(ScheduledTask scheduledTask) {
- for (Player player : Bukkit.getOnlinePlayers()) {
- if (!FloodgateApi.getInstance().isFloodgatePlayer(player.getUniqueId())) continue;
+ for (UUID playerUUID : plugin.getModelManager().getPlayerJoinedCache()) {
+ Player player = Bukkit.getPlayer(playerUUID);
float pitch = player.getLocation().getPitch();
Pair seat = plugin.getModelManager().getDriversCache().get(player.getUniqueId());
diff --git a/src/main/java/re/imc/geysermodelengine/runnables/EntityTaskRunnable.java b/src/main/java/re/imc/geysermodelengine/runnables/EntityTaskRunnable.java
deleted file mode 100644
index fd92295..0000000
--- a/src/main/java/re/imc/geysermodelengine/runnables/EntityTaskRunnable.java
+++ /dev/null
@@ -1,268 +0,0 @@
-package re.imc.geysermodelengine.runnables;
-
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.ticxo.modelengine.api.animation.BlueprintAnimation;
-import com.ticxo.modelengine.api.animation.handler.AnimationHandler;
-import com.ticxo.modelengine.api.generator.blueprint.BlueprintBone;
-import com.ticxo.modelengine.api.model.ActiveModel;
-import com.ticxo.modelengine.api.model.ModeledEntity;
-import com.ticxo.modelengine.api.model.bone.ModelBone;
-import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
-import org.bukkit.entity.Player;
-import re.imc.geysermodelengine.GeyserModelEngine;
-import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
-import re.imc.geysermodelengine.packet.entity.PacketEntity;
-import re.imc.geysermodelengine.util.BooleanPacker;
-
-import java.awt.*;
-import java.util.*;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-public class EntityTaskRunnable {
-
- private final GeyserModelEngine plugin;
-
- private final ModelEntityData model;
-
- private int tick = 0;
- private int syncTick = 0;
-
- private float lastScale = -1.0f;
- private Color lastColor = null;
-
- private boolean removed = false;
-
- private final ConcurrentHashMap lastIntSet = new ConcurrentHashMap<>();
- private final Cache lastPlayedAnim = CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MILLISECONDS).build();
-
- private final BooleanPacker booleanPacker = new BooleanPacker();
-
- private final ScheduledFuture scheduledFuture;
-
- public EntityTaskRunnable(GeyserModelEngine plugin, ModelEntityData model) {
- this.plugin = plugin;
-
- this.model = model;
-
- plugin.getEntityTaskManager().sendHitBoxToAll(model);
-
- scheduledFuture = plugin.getSchedulerPool().scheduleAtFixedRate(this::runAsync, 0, 20, TimeUnit.MILLISECONDS);
- }
-
- public void runAsync() {
- plugin.getEntityTaskManager().checkViewers(model, model.getViewers());
-
- PacketEntity entity = model.getEntity();
- if (entity.isDead()) return;
-
- model.teleportToModel();
-
- Set viewers = model.getViewers();
- ActiveModel activeModel = model.getActiveModel();
- ModeledEntity modeledEntity = model.getModeledEntity();
-
- if (activeModel.isDestroyed() || activeModel.isRemoved()) {
- removed = true;
- entity.remove();
-
- plugin.getModelManager().getEntitiesCache().remove(modeledEntity.getBase().getEntityId());
- plugin.getModelManager().getModelEntitiesCache().remove(entity.getEntityId());
- cancel();
- return;
- }
-
- if (tick % 5 == 0) {
- if (tick % 40 == 0) {
- for (Player viewer : Set.copyOf(viewers)) {
- if (!plugin.getEntityTaskManager().canSee(viewer, model.getEntity())) {
- viewers.remove(viewer);
- }
- }
- }
- }
-
- tick ++;
- if (tick > 400) {
- tick = 0;
- plugin.getEntityTaskManager().sendHitBoxToAll(model);
- }
-
- if (viewers.isEmpty()) return;
-
- plugin.getEntityTaskManager().sendScale(model, viewers, lastScale, false);
- plugin.getEntityTaskManager().sendColor(model, viewers, lastColor, false);
- }
-
- public void cancel() {
- scheduledFuture.cancel(true);
- }
-
- public void sendEntityData(ModelEntityData model, Player player, int delay) {
- EntityUtils.setCustomEntity(player, model.getEntity().getEntityId(), plugin.getConfigManager().getConfig().getString("namespace") + ":" + model.getActiveModel().getBlueprint().getName().toLowerCase());
-
- plugin.getSchedulerPool().schedule(() -> {
- model.getEntity().sendSpawnPacket(Collections.singletonList(player));
-
- plugin.getSchedulerPool().schedule(() -> {
- plugin.getEntityTaskManager().sendHitBox(model, player);
- plugin.getEntityTaskManager().sendScale(model, Collections.singleton(player), lastScale, true);
- plugin.getEntityTaskManager().sendColor(model, Collections.singleton(player), lastColor, true);
-
- updateEntityProperties(model, Collections.singleton(player), true);
- }, 500, TimeUnit.MILLISECONDS);
- }, delay * 50L, TimeUnit.MILLISECONDS);
- }
-
- public void updateEntityProperties(ModelEntityData model, Collection players, boolean firstSend, String... forceAnims) {
- int entity = model.getEntity().getEntityId();
- Set forceAnimSet = Set.of(forceAnims);
-
- Map boneUpdates = new HashMap<>();
- Map animUpdates = new HashMap<>();
- Set anims = new HashSet<>();
-
- model.getActiveModel().getBlueprint().getBones().forEach((s, bone) -> processBone(model, bone, boneUpdates));
-
- AnimationHandler handler = model.getActiveModel().getAnimationHandler();
- Set priority = model.getActiveModel().getBlueprint().getAnimationDescendingPriority();
- for (String animId : priority) {
- if (handler.isPlayingAnimation(animId)) {
- BlueprintAnimation anim = model.getActiveModel().getBlueprint().getAnimations().get(animId);
-
- anims.add(animId);
- if (anim.isOverride() && anim.getLoopMode() == BlueprintAnimation.LoopMode.ONCE) {
- break;
- }
- }
- }
-
- for (String id : priority) {
- if (anims.contains(id)) {
- animUpdates.put(id, true);
- } else {
- animUpdates.put(id, false);
- }
- }
-
- Set lastPlayed = new HashSet<>(lastPlayedAnim.asMap().keySet());
-
- for (Map.Entry anim : animUpdates.entrySet()) {
- if (anim.getValue()) {
- lastPlayedAnim.put(anim.getKey(), true);
- }
- }
-
- for (String anim : lastPlayed) animUpdates.put(anim, true);
-
- if (boneUpdates.isEmpty() && animUpdates.isEmpty()) return;
-
- Map intUpdates = new HashMap<>();
- int i = 0;
-
- for (Integer integer : booleanPacker.mapBooleansToInts(boneUpdates)) {
- intUpdates.put(plugin.getConfigManager().getConfig().getString("namespace") + ":bone" + i, integer);
- i++;
- }
-
- i = 0;
- for (Integer integer : booleanPacker.mapBooleansToInts(animUpdates)) {
- intUpdates.put(plugin.getConfigManager().getConfig().getString("namespace") + ":anim" + i, integer);
- i++;
- }
-
- if (!firstSend) {
- if (intUpdates.equals(lastIntSet)) {
- return;
- } else {
- lastIntSet.clear();
- lastIntSet.putAll(intUpdates);
- }
- }
-
- if (plugin.getConfigManager().getConfig().getBoolean("debug")) plugin.getLogger().info(animUpdates.toString());
-
- List list = new ArrayList<>(boneUpdates.keySet());
- Collections.sort(list);
-
- for (Player player : players) {
- EntityUtils.sendIntProperties(player, entity, intUpdates);
- }
- }
-
- private void processBone(ModelEntityData model, BlueprintBone bone, Map map) {
- String name = plugin.getEntityTaskManager().unstripName(bone).toLowerCase();
- if (name.equals("hitbox") ||
- name.equals("shadow") ||
- name.equals("mount") ||
- name.startsWith("p_") ||
- name.startsWith("b_") ||
- name.startsWith("ob_")) {
- return;
- }
-
- for (BlueprintBone blueprintBone : bone.getChildren().values()) processBone(model, blueprintBone, map);
-
- ModelBone activeBone = model.getActiveModel().getBones().get(bone.getName());
-
- boolean visible = false;
- if (activeBone != null) visible = activeBone.isVisible();
-
- map.put(name, visible);
- }
-
- public void setTick(int tick) {
- this.tick = tick;
- }
-
- public void setSyncTick(int syncTick) {
- this.syncTick = syncTick;
- }
-
- public void setRemoved(boolean removed) {
- this.removed = removed;
- }
-
- public void setLastScale(float lastScale) {
- this.lastScale = lastScale;
- }
-
- public int getTick() {
- return tick;
- }
-
- public int getSyncTick() {
- return syncTick;
- }
-
- public void setLastColor(Color lastColor) {
- this.lastColor = lastColor;
- }
-
- public float getLastScale() {
- return lastScale;
- }
-
- public Color getLastColor() {
- return lastColor;
- }
-
- public boolean isRemoved() {
- return removed;
- }
-
- public ConcurrentHashMap getLastIntSet() {
- return lastIntSet;
- }
-
- public Cache getLastPlayedAnim() {
- return lastPlayedAnim;
- }
-
- public ScheduledFuture getScheduledFuture() {
- return scheduledFuture;
- }
-}
diff --git a/src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java b/src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java
index 015682b..bf48f5d 100644
--- a/src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java
+++ b/src/main/java/re/imc/geysermodelengine/runnables/UpdateTaskRunnable.java
@@ -1,9 +1,9 @@
package re.imc.geysermodelengine.runnables;
-import com.ticxo.modelengine.api.model.ActiveModel;
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
import re.imc.geysermodelengine.GeyserModelEngine;
-import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
+import re.imc.geysermodelengine.managers.model.entity.EntityData;
+import re.imc.geysermodelengine.managers.model.model.Model;
import java.util.Map;
import java.util.function.Consumer;
@@ -19,8 +19,8 @@ public class UpdateTaskRunnable implements Consumer {
@Override
public void accept(ScheduledTask scheduledTask) {
try {
- for (Map models : plugin.getModelManager().getEntitiesCache().values()) {
- models.values().forEach(model -> model.getEntityTask().updateEntityProperties(model, model.getViewers(), false));
+ for (Map models : plugin.getModelManager().getEntitiesCache().values()) {
+ models.values().forEach(entityData -> plugin.getEntityTaskManager().getPropertyHandler().updateEntityProperties(entityData, entityData.getViewers(), false));
}
} catch (Throwable err) {
throw new RuntimeException(err);
diff --git a/src/main/java/re/imc/geysermodelengine/util/BooleanPacker.java b/src/main/java/re/imc/geysermodelengine/util/BooleanPacker.java
index 5692a9d..ee2fe85 100644
--- a/src/main/java/re/imc/geysermodelengine/util/BooleanPacker.java
+++ b/src/main/java/re/imc/geysermodelengine/util/BooleanPacker.java
@@ -7,9 +7,9 @@ import java.util.Map;
public class BooleanPacker {
- private final int MAX_BOOLEANS = 24;
+ private static final int MAX_BOOLEANS = 24;
- public int booleansToInt(List booleans) {
+ public static int booleansToInt(List booleans) {
int result = 0;
int i = 1;
@@ -23,7 +23,7 @@ public class BooleanPacker {
return result;
}
- public int mapBooleansToInt(Map booleanMap) {
+ public static int mapBooleansToInt(Map booleanMap) {
int result = 0;
int i = 1;
@@ -39,7 +39,7 @@ public class BooleanPacker {
return result;
}
- public List booleansToInts(List booleans) {
+ public static List booleansToInts(List booleans) {
List results = new ArrayList<>();
int result = 0;
int i = 1;
@@ -62,7 +62,7 @@ public class BooleanPacker {
return results;
}
- public List mapBooleansToInts(Map booleanMap) {
+ public static List mapBooleansToInts(Map booleanMap) {
List keys = new ArrayList<>(booleanMap.keySet());
List booleans = new ArrayList<>();
diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml
index ff07ed8..32d3d21 100644
--- a/src/main/resources/config.yml
+++ b/src/main/resources/config.yml
@@ -1,13 +1,13 @@
-bstats: true
+metrics:
+ bstats: true
-namespace: "modelengine"
-data-send-delay: 5
-entity-view-distance: 50
-join-send-delay: 20
-entity-position-update-period: 35
-thread-pool-size: 4
-model-entity-type: BAT # must be a living entity
-enable-part-visibility-models:
- - example
+models:
+ namespace: "modelengine"
+ data-send-delay: 5
+ entity-view-distance: 50
+ join-send-delay: 20
+ entity-position-update-period: 35
+ thread-pool-size: 4
-debug: false
\ No newline at end of file
+options:
+ debug: false
\ No newline at end of file
diff --git a/src/main/resources/paper-plugin.yml b/src/main/resources/paper-plugin.yml
index dcb6411..d98343d 100644
--- a/src/main/resources/paper-plugin.yml
+++ b/src/main/resources/paper-plugin.yml
@@ -16,7 +16,9 @@ dependencies:
required: true
packetevents:
required: true
- ModelEngine:
- required: true
floodgate:
- required: true
\ No newline at end of file
+ required: true
+ ModelEngine:
+ required: false
+ BetterModel:
+ required: false
\ No newline at end of file