mirror of
https://github.com/xSquishyLiam/mc-GeyserModelEngine-plugin.git
synced 2025-12-24 01:09:27 +00:00
Major Overhaul (needs testing)
This commit is contained in:
@@ -2,149 +2,143 @@ package re.imc.geysermodelengine;
|
||||
|
||||
import com.github.retrooper.packetevents.PacketEvents;
|
||||
import com.github.retrooper.packetevents.event.PacketListenerPriority;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
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 dev.jorel.commandapi.CommandAPI;
|
||||
import dev.jorel.commandapi.CommandAPIBukkitConfig;
|
||||
import io.github.retrooper.packetevents.factory.spigot.SpigotPacketEventsBuilder;
|
||||
import lombok.Getter;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import re.imc.geysermodelengine.commands.ReloadCommand;
|
||||
import re.imc.geysermodelengine.listener.ModelListener;
|
||||
import re.imc.geysermodelengine.listener.MountPacketListener;
|
||||
import re.imc.geysermodelengine.model.BedrockMountControl;
|
||||
import re.imc.geysermodelengine.model.ModelEntity;
|
||||
import re.imc.geysermodelengine.managers.ConfigManager;
|
||||
import re.imc.geysermodelengine.managers.bedrock.BedrockMountControlManager;
|
||||
import re.imc.geysermodelengine.managers.commands.CommandManager;
|
||||
import re.imc.geysermodelengine.managers.model.EntityTaskManager;
|
||||
import re.imc.geysermodelengine.managers.model.ModelManager;
|
||||
import re.imc.geysermodelengine.managers.player.PlayerManager;
|
||||
import re.imc.geysermodelengine.managers.server.ServerManager;
|
||||
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
|
||||
import re.imc.geysermodelengine.runnables.BedrockMountControlRunnable;
|
||||
import re.imc.geysermodelengine.runnables.UpdateTaskRunnable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public final class GeyserModelEngine extends JavaPlugin {
|
||||
public class GeyserModelEngine extends JavaPlugin {
|
||||
|
||||
@Getter
|
||||
private static GeyserModelEngine instance;
|
||||
private ConfigManager configManager;
|
||||
private ServerManager serverManager;
|
||||
|
||||
@Getter
|
||||
private static boolean alwaysSendSkin;
|
||||
private CommandManager commandManager;
|
||||
|
||||
@Getter
|
||||
private int sendDelay;
|
||||
private ModelManager modelManager;
|
||||
private EntityTaskManager entityTaskManager;
|
||||
private BedrockMountControlManager bedrockMountControlManager;
|
||||
|
||||
@Getter
|
||||
private int viewDistance;
|
||||
|
||||
@Getter
|
||||
private Set<Player> joinedPlayers = new HashSet<>();
|
||||
|
||||
@Getter
|
||||
private int joinSendDelay;
|
||||
|
||||
@Getter
|
||||
private long entityPositionUpdatePeriod;
|
||||
|
||||
@Getter
|
||||
private boolean debug;
|
||||
|
||||
@Getter
|
||||
private Map<Player, Pair<ActiveModel, Mount>> drivers = new ConcurrentHashMap<>();
|
||||
|
||||
@Getter
|
||||
private boolean initialized = false;
|
||||
|
||||
@Getter
|
||||
private List<String> enablePartVisibilityModels = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
private ScheduledExecutorService scheduler;
|
||||
private ScheduledFuture<?> updateTask;
|
||||
private PlayerManager playerManager;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
PacketEvents.setAPI(SpigotPacketEventsBuilder.build(this));
|
||||
PacketEvents.getAPI().load();
|
||||
|
||||
CommandAPI.onLoad(new CommandAPIBukkitConfig(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
PacketEvents.getAPI().init();
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new MountPacketListener(), PacketListenerPriority.NORMAL);
|
||||
/*
|
||||
scheduler.scheduleAtFixedRate(() -> {
|
||||
try {
|
||||
for (Map<ActiveModel, ModelEntity> models : ModelEntity.ENTITIES.values()) {
|
||||
models.values().forEach(ModelEntity::teleportToModel);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
}, 10, entityPositionUpdatePeriod, TimeUnit.MILLISECONDS);
|
||||
loadHooks();
|
||||
loadManagers();
|
||||
loadRunnables();
|
||||
|
||||
*/
|
||||
PacketEvents.getAPI().getEventManager().registerListener(new MountPacketListener(this), PacketListenerPriority.NORMAL);
|
||||
|
||||
reload();
|
||||
getCommand("geysermodelengine").setExecutor(new ReloadCommand(this));
|
||||
Bukkit.getPluginManager().registerEvents(new ModelListener(), this);
|
||||
Bukkit.getScheduler()
|
||||
.runTaskLater(GeyserModelEngine.getInstance(), () -> {
|
||||
Bukkit.getPluginManager().registerEvents(new ModelListener(this), this);
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(this, () -> {
|
||||
for (World world : Bukkit.getWorlds()) {
|
||||
|
||||
for (Entity entity : world.getEntities()) {
|
||||
if (!ModelEntity.ENTITIES.containsKey(entity.getEntityId())) {
|
||||
|
||||
if (!modelManager.getEntitiesCache().containsKey(entity.getEntityId())) {
|
||||
|
||||
ModeledEntity modeledEntity = ModelEngineAPI.getModeledEntity(entity);
|
||||
|
||||
if (modeledEntity != null) {
|
||||
Optional<ActiveModel> model = modeledEntity.getModels().values().stream().findFirst();
|
||||
model.ifPresent(m -> ModelEntity.create(modeledEntity, m));
|
||||
model.ifPresent(m -> modelManager.create(modeledEntity, m));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
}, 100);
|
||||
|
||||
|
||||
BedrockMountControl.startTask();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
saveDefaultConfig();
|
||||
// alwaysSendSkin = getConfig().getBoolean("always-send-skin");
|
||||
sendDelay = getConfig().getInt("data-send-delay", 5);
|
||||
scheduler = Executors.newScheduledThreadPool(getConfig().getInt("thread-pool-size", 4));
|
||||
viewDistance = getConfig().getInt("entity-view-distance", 60);
|
||||
debug = getConfig().getBoolean("debug", false);
|
||||
joinSendDelay = getConfig().getInt("join-send-delay", 20);
|
||||
entityPositionUpdatePeriod = getConfig().getLong("entity-position-update-period", 35);
|
||||
enablePartVisibilityModels.addAll(getConfig().getStringList("enable-part-visibility-models"));
|
||||
|
||||
instance = this;
|
||||
if (updateTask != null) updateTask.cancel(true);
|
||||
|
||||
updateTask = scheduler.scheduleWithFixedDelay(() -> {
|
||||
try {
|
||||
for (Map<ActiveModel, ModelEntity> models : ModelEntity.ENTITIES.values()) {
|
||||
models.values().forEach(model -> model.getTask().updateEntityProperties(model.getViewers(), false));
|
||||
}
|
||||
} catch (Throwable err) {
|
||||
throw new RuntimeException(err);
|
||||
}
|
||||
}, 10, entityPositionUpdatePeriod, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
PacketEvents.getAPI().terminate();
|
||||
for (Map<ActiveModel, ModelEntity> entities : ModelEntity.ENTITIES.values()) {
|
||||
|
||||
for (Map<ActiveModel, ModelEntityData> entities : modelManager.getEntitiesCache().values()) {
|
||||
entities.forEach((model, modelEntity) -> {
|
||||
modelEntity.getEntity().remove();
|
||||
});
|
||||
}
|
||||
// Plugin shutdown logic
|
||||
|
||||
CommandAPI.onDisable();
|
||||
}
|
||||
|
||||
private void loadHooks() {
|
||||
PacketEvents.getAPI().init();
|
||||
CommandAPI.onEnable();
|
||||
}
|
||||
|
||||
private void loadManagers() {
|
||||
this.configManager = new ConfigManager(this);
|
||||
this.serverManager = new ServerManager();
|
||||
|
||||
this.commandManager = new CommandManager(this);
|
||||
|
||||
this.modelManager = new ModelManager(this);
|
||||
this.entityTaskManager = new EntityTaskManager(this);
|
||||
this.bedrockMountControlManager = new BedrockMountControlManager();
|
||||
|
||||
this.playerManager = new PlayerManager();
|
||||
}
|
||||
|
||||
private void loadRunnables() {
|
||||
Bukkit.getAsyncScheduler().runAtFixedRate(this, new UpdateTaskRunnable(this), 10, configManager.getConfig().getLong("entity-position-update-period"), TimeUnit.MILLISECONDS);
|
||||
Bukkit.getAsyncScheduler().runAtFixedRate(this, new BedrockMountControlRunnable(this), 1, 1, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public ConfigManager getConfigManager() {
|
||||
return configManager;
|
||||
}
|
||||
|
||||
public ServerManager getServerManager() {
|
||||
return serverManager;
|
||||
}
|
||||
|
||||
public CommandManager getCommandManager() {
|
||||
return commandManager;
|
||||
}
|
||||
|
||||
public ModelManager getModelManager() {
|
||||
return modelManager;
|
||||
}
|
||||
|
||||
public EntityTaskManager getEntityTaskManager() {
|
||||
return entityTaskManager;
|
||||
}
|
||||
|
||||
public BedrockMountControlManager getBedrockMountControlManager() {
|
||||
return bedrockMountControlManager;
|
||||
}
|
||||
|
||||
public PlayerManager getPlayerManager() {
|
||||
return playerManager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package re.imc.geysermodelengine.commands;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||
|
||||
public class ReloadCommand implements CommandExecutor {
|
||||
|
||||
private final GeyserModelEngine plugin;
|
||||
|
||||
public ReloadCommand(GeyserModelEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
|
||||
if (sender instanceof Player && !sender.hasPermission("geysermodelengine.reload")) {
|
||||
sender.sendMessage("§cYou don't have permission to use this command.");
|
||||
return true;
|
||||
}
|
||||
|
||||
plugin.reloadConfig();
|
||||
plugin.reload();
|
||||
|
||||
sender.sendMessage("§aGeyserModelEngine configuration reloaded!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package re.imc.geysermodelengine.commands.geysermodelenginecommands;
|
||||
|
||||
import dev.jorel.commandapi.CommandAPICommand;
|
||||
import org.bukkit.Bukkit;
|
||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||
import re.imc.geysermodelengine.managers.commands.subcommands.SubCommands;
|
||||
import re.imc.geysermodelengine.util.ColourUtils;
|
||||
|
||||
public class GeyserModelEngineReloadCommand implements SubCommands {
|
||||
|
||||
private final GeyserModelEngine plugin;
|
||||
|
||||
private final ColourUtils colourUtils = new ColourUtils();
|
||||
|
||||
public GeyserModelEngineReloadCommand(GeyserModelEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandAPICommand onCommand() {
|
||||
return new CommandAPICommand("reload")
|
||||
.withPermission("geysermodelengine.commands.reload")
|
||||
.executes((sender, args) -> {
|
||||
Bukkit.getAsyncScheduler().runNow(plugin, scheduledTask -> {
|
||||
plugin.getConfigManager().load();
|
||||
});
|
||||
|
||||
sender.sendMessage(colourUtils.miniFormat(plugin.getConfigManager().getLang().getString("commands.reload.successfully-reloaded")));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package re.imc.geysermodelengine.listener;
|
||||
import com.ticxo.modelengine.api.events.AddModelEvent;
|
||||
import com.ticxo.modelengine.api.events.ModelDismountEvent;
|
||||
import com.ticxo.modelengine.api.events.ModelMountEvent;
|
||||
import com.ticxo.modelengine.api.events.RemoveModelEvent;
|
||||
import com.ticxo.modelengine.api.model.ActiveModel;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -12,117 +11,54 @@ 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.PlayerLoginEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||
import re.imc.geysermodelengine.model.ModelEntity;
|
||||
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ModelListener implements Listener {
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onAddModel(AddModelEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
private final GeyserModelEngine plugin;
|
||||
|
||||
if (!GeyserModelEngine.getInstance().isInitialized()) {
|
||||
return;
|
||||
}
|
||||
ModelEntity.create(event.getTarget(), event.getModel());
|
||||
public ModelListener(GeyserModelEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onAddModel(AddModelEvent event) {
|
||||
if (event.isCancelled()) return;
|
||||
|
||||
@EventHandler
|
||||
public void onRemoveModel(RemoveModelEvent event) {
|
||||
plugin.getModelManager().create(event.getTarget(), event.getModel());
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onModelMount(ModelMountEvent event) {
|
||||
Map<ActiveModel, ModelEntity> map = ModelEntity.ENTITIES.get(event.getVehicle().getModeledEntity().getBase().getEntityId());
|
||||
if (map == null) {
|
||||
}
|
||||
if (!event.isDriver()) {
|
||||
return;
|
||||
}
|
||||
ModelEntity model = map.get(event.getVehicle());
|
||||
Map<ActiveModel, ModelEntityData> 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) {
|
||||
GeyserModelEngine.getInstance().getDrivers().put(player, Pair.of(event.getVehicle(), event.getSeat()));
|
||||
plugin.getBedrockMountControlManager().getDriversCache().put(player, Pair.of(event.getVehicle(), event.getSeat()));
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onModelDismount(ModelDismountEvent event) {
|
||||
if (event.getPassenger() instanceof Player player) {
|
||||
GeyserModelEngine.getInstance().getDrivers().remove(player);
|
||||
plugin.getBedrockMountControlManager().getDriversCache().remove(player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@EventHandler(priority = EventPriority.MONITOR)
|
||||
public void onModelEntityHurt(EntityDamageEvent event) {
|
||||
if (event.isCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<ActiveModel, ModelEntity> model = ModelEntity.ENTITIES.get(event.getEntity().getEntityId());
|
||||
if (model != null) {
|
||||
for (Map.Entry<ActiveModel, ModelEntity> entry : model.entrySet()) {
|
||||
if (!entry.getValue().getEntity().isDead()) {
|
||||
//entry.getValue().getEntity().sendHurtPacket(entry.getValue().getViewers());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@EventHandler
|
||||
public void onModelAttack(EntityDamageByEntityEvent event) {
|
||||
ModelEntity model = ModelEntity.ENTITIES.get(event.getDamager().getEntityId());
|
||||
if (model != null) {
|
||||
EntityTask task = model.getTask();
|
||||
|
||||
task.playAnimation("attack", 55);
|
||||
}
|
||||
}|
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onAnimationPlay(AnimationPlayEvent event) {
|
||||
Map<ActiveModel, ModelEntity> map = ModelEntity.ENTITIES.get(event.getModel().getModeledEntity().getBase().getEntityId());
|
||||
if (map == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModelEntity model = map.get(event.getModel());
|
||||
model.getTask().updateEntityProperties(model.getViewers(), false, event.getProperty().getName());
|
||||
}
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onAnimationEnd(AnimationEndEvent event) {
|
||||
Map<ActiveModel, ModelEntity> map = ModelEntity.ENTITIES.get(event.getModel().getModeledEntity().getBase().getEntityId());
|
||||
if (map == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModelEntity model = map.get(event.getModel());
|
||||
model.getTask().updateEntityProperties(model.getViewers(), false, event.getProperty().);
|
||||
}
|
||||
*/
|
||||
//TODO Find out why we need this bc uh what?
|
||||
@EventHandler
|
||||
public void onPlayerLogin(PlayerJoinEvent event) {
|
||||
Bukkit.getScheduler().runTaskLater(GeyserModelEngine.getInstance(), () -> GeyserModelEngine.getInstance().getJoinedPlayers().add(event.getPlayer()), 10);
|
||||
Bukkit.getGlobalRegionScheduler().runDelayed(plugin, scheduledTask -> plugin.getPlayerManager().getPlayerJoinedCache().add(event.getPlayer()), 10);
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
Bukkit.getScheduler().runTaskLater(GeyserModelEngine.getInstance(), () -> GeyserModelEngine.getInstance().getJoinedPlayers().remove(event.getPlayer()), 10);
|
||||
Bukkit.getGlobalRegionScheduler().runDelayed(plugin, scheduledTask -> plugin.getPlayerManager().getPlayerJoinedCache().remove(event.getPlayer()), 10);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,12 @@ import re.imc.geysermodelengine.GeyserModelEngine;
|
||||
|
||||
public class MountPacketListener implements PacketListener {
|
||||
|
||||
private final GeyserModelEngine plugin;
|
||||
|
||||
public MountPacketListener(GeyserModelEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPacketReceive(PacketReceiveEvent event) {
|
||||
if (event.getPacketType() != PacketType.Play.Client.ENTITY_ACTION) return;
|
||||
@@ -22,7 +28,7 @@ public class MountPacketListener implements PacketListener {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
WrapperPlayClientEntityAction action = new WrapperPlayClientEntityAction(event);
|
||||
Pair<ActiveModel, Mount> seat = GeyserModelEngine.getInstance().getDrivers().get(player);
|
||||
Pair<ActiveModel, Mount> seat = plugin.getBedrockMountControlManager().getDriversCache().get(player);
|
||||
|
||||
if (seat == null) return;
|
||||
if (action.getAction() != WrapperPlayClientEntityAction.Action.START_SNEAKING) return;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package re.imc.geysermodelengine.managers;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ConfigManager {
|
||||
|
||||
private final GeyserModelEngine plugin;
|
||||
|
||||
private FileConfiguration config, lang;
|
||||
|
||||
public ConfigManager(GeyserModelEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
load();
|
||||
}
|
||||
|
||||
public void load() {
|
||||
this.config = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), "config.yml"));
|
||||
this.lang = YamlConfiguration.loadConfiguration(new File(plugin.getDataFolder(), "Lang/messages.yml"));
|
||||
}
|
||||
|
||||
public FileConfiguration getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public FileConfiguration getLang() {
|
||||
return lang;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package re.imc.geysermodelengine.managers.bedrock;
|
||||
|
||||
import com.ticxo.modelengine.api.model.ActiveModel;
|
||||
import com.ticxo.modelengine.api.model.bone.type.Mount;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class BedrockMountControlManager {
|
||||
|
||||
private final ConcurrentHashMap<Player, Pair<ActiveModel, Mount>> driversCache = new ConcurrentHashMap<>();
|
||||
|
||||
public ConcurrentHashMap<Player, Pair<ActiveModel, Mount>> getDriversCache() {
|
||||
return driversCache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package re.imc.geysermodelengine.managers.commands;
|
||||
|
||||
import org.reflections.Reflections;
|
||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class CommandManager {
|
||||
|
||||
private final GeyserModelEngine plugin;
|
||||
|
||||
private final HashMap<String, CommandManagers> commandManagersCache = new HashMap<>();
|
||||
|
||||
public CommandManager(GeyserModelEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
load("re.imc.geysermodelengine.managers.commands.managers");
|
||||
}
|
||||
|
||||
private void load(String path) {
|
||||
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);
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException err) {
|
||||
plugin.getLogger().severe("Failed to load Command Manager " + clazz.getName());
|
||||
throw new RuntimeException(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public HashMap<String, CommandManagers> getCommandManagersCache() {
|
||||
return commandManagersCache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package re.imc.geysermodelengine.managers.commands;
|
||||
|
||||
import re.imc.geysermodelengine.managers.commands.subcommands.SubCommands;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public interface CommandManagers {
|
||||
|
||||
String name();
|
||||
|
||||
ArrayList<SubCommands> getCommands();
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package re.imc.geysermodelengine.managers.commands.managers.geysermodelengine;
|
||||
|
||||
import dev.jorel.commandapi.CommandAPICommand;
|
||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||
import re.imc.geysermodelengine.commands.geysermodelenginecommands.GeyserModelEngineReloadCommand;
|
||||
import re.imc.geysermodelengine.managers.commands.CommandManagers;
|
||||
import re.imc.geysermodelengine.managers.commands.subcommands.SubCommands;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GeyserModelEngineCommandManager implements CommandManagers {
|
||||
|
||||
private final ArrayList<SubCommands> commands = new ArrayList<>();
|
||||
|
||||
public GeyserModelEngineCommandManager(GeyserModelEngine plugin) {
|
||||
commands.add(new GeyserModelEngineReloadCommand(plugin));
|
||||
|
||||
registerCommand();
|
||||
}
|
||||
|
||||
private void registerCommand() {
|
||||
CommandAPICommand geyserModelEngineCommand = new CommandAPICommand(name());
|
||||
|
||||
commands.forEach(subCommands -> geyserModelEngineCommand.withSubcommand(subCommands.onCommand()));
|
||||
|
||||
geyserModelEngineCommand.register();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "geysermodelengine";
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArrayList<SubCommands> getCommands() {
|
||||
return commands;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package re.imc.geysermodelengine.managers.commands.subcommands;
|
||||
|
||||
import dev.jorel.commandapi.CommandAPICommand;
|
||||
|
||||
public interface SubCommands {
|
||||
CommandAPICommand onCommand();
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
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.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;
|
||||
|
||||
public EntityTaskManager(GeyserModelEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
|
||||
try {
|
||||
this.scaleMethod = ActiveModel.class.getMethod("getScale");
|
||||
} catch (NoSuchMethodException err) {
|
||||
throw new RuntimeException(err);
|
||||
}
|
||||
}
|
||||
|
||||
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<Player> 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 t) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public void sendColor(ModelEntityData model, Collection<Player> 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<Player> viewers) {
|
||||
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
|
||||
if (FloodgateApi.getInstance().isFloodgatePlayer(onlinePlayer.getUniqueId())) {
|
||||
|
||||
if (canSee(onlinePlayer, model.getEntity())) {
|
||||
|
||||
if (!viewers.contains(onlinePlayer)) {
|
||||
sendSpawnPacket(model, onlinePlayer);
|
||||
viewers.add(onlinePlayer);
|
||||
}
|
||||
} else {
|
||||
if (viewers.contains(onlinePlayer)) {
|
||||
model.getEntity().sendEntityDestroyPacket(Collections.singletonList(onlinePlayer));
|
||||
viewers.remove(onlinePlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendSpawnPacket(ModelEntityData model, Player onlinePlayer) {
|
||||
EntityTaskRunnable task = model.getEntityTask();
|
||||
boolean firstJoined = !plugin.getPlayerManager().getPlayerJoinedCache().contains(onlinePlayer);
|
||||
|
||||
if (firstJoined) {
|
||||
task.sendEntityData(model, onlinePlayer, plugin.getConfigManager().getConfig().getInt("join-send-delay") / 50);
|
||||
} else {
|
||||
task.sendEntityData(model, onlinePlayer, 5);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canSee(Player player, PacketEntity entity) {
|
||||
if (!player.isOnline()) return false;
|
||||
if (!plugin.getPlayerManager().getPlayerJoinedCache().contains(player)) return false;
|
||||
|
||||
Location playerLocation = player.getLocation().clone();
|
||||
Location entityLocation = entity.getLocation().clone();
|
||||
playerLocation.setY(0);
|
||||
entityLocation.setY(0);
|
||||
|
||||
if (playerLocation.getWorld() != entityLocation.getWorld()) return false;
|
||||
if (playerLocation.distanceSquared(entityLocation) > player.getSendViewDistance() * player.getSendViewDistance() * 48) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void sendHitBoxToAll(ModelEntityData 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) {
|
||||
ActiveModel activeModel = model.getActiveModel();
|
||||
BlueprintAnimation animationProperty = activeModel.getBlueprint().getAnimations().get(animation);
|
||||
return !(animationProperty == null);
|
||||
}
|
||||
|
||||
public Method getScaleMethod() {
|
||||
return scaleMethod;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package re.imc.geysermodelengine.managers.model;
|
||||
|
||||
import com.ticxo.modelengine.api.model.ActiveModel;
|
||||
import com.ticxo.modelengine.api.model.ModeledEntity;
|
||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||
import re.imc.geysermodelengine.managers.model.data.ModelEntityData;
|
||||
import re.imc.geysermodelengine.runnables.EntityTaskRunnable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ModelManager {
|
||||
|
||||
private final GeyserModelEngine plugin;
|
||||
|
||||
private final ConcurrentHashMap<Integer, Map<ActiveModel, ModelEntityData>> entitiesCache = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, ModelEntityData> modelEntitiesCache = 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<ActiveModel, ModelEntityData> map = entitiesCache.computeIfAbsent(id, k -> new HashMap<>());
|
||||
|
||||
for (Map.Entry<ActiveModel, ModelEntityData> entry : map.entrySet()) {
|
||||
if (entry.getKey() != model && entry.getKey().getBlueprint().getName().equals(model.getBlueprint().getName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
map.put(model, modelEntity);
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<Integer, Map<ActiveModel, ModelEntityData>> getEntitiesCache() {
|
||||
return entitiesCache;
|
||||
}
|
||||
|
||||
public Map<Integer, ModelEntityData> getModelEntitiesCache() {
|
||||
return modelEntitiesCache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package re.imc.geysermodelengine.managers.model.data;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.ticxo.modelengine.api.model.ActiveModel;
|
||||
import com.ticxo.modelengine.api.model.ModeledEntity;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||
import re.imc.geysermodelengine.packet.entity.PacketEntity;
|
||||
import re.imc.geysermodelengine.runnables.EntityTaskRunnable;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ModelEntityData {
|
||||
|
||||
private final GeyserModelEngine plugin;
|
||||
|
||||
private PacketEntity entity;
|
||||
|
||||
private final Set<Player> viewers = Sets.newConcurrentHashSet();
|
||||
|
||||
private final ModeledEntity modeledEntity;
|
||||
|
||||
private final ActiveModel activeModel;
|
||||
|
||||
private EntityTaskRunnable entityTask;
|
||||
|
||||
public ModelEntityData(GeyserModelEngine plugin, ModeledEntity modeledEntity, ActiveModel model) {
|
||||
this.plugin = plugin;
|
||||
|
||||
this.modeledEntity = modeledEntity;
|
||||
this.activeModel = model;
|
||||
this.entity = spawnEntity();
|
||||
|
||||
runEntityTask();
|
||||
}
|
||||
|
||||
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);
|
||||
Bukkit.getAsyncScheduler().runAtFixedRate(plugin, entityTask, 0, 20, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public PacketEntity getEntity() {
|
||||
return entity;
|
||||
}
|
||||
|
||||
public Set<Player> getViewers() {
|
||||
return viewers;
|
||||
}
|
||||
|
||||
public ModeledEntity getModeledEntity() {
|
||||
return modeledEntity;
|
||||
}
|
||||
|
||||
public ActiveModel getActiveModel() {
|
||||
return activeModel;
|
||||
}
|
||||
|
||||
public EntityTaskRunnable getEntityTask() {
|
||||
return entityTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package re.imc.geysermodelengine.managers.player;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
||||
public class PlayerManager {
|
||||
|
||||
private final HashSet<Player> playerJoinedCache = new HashSet<>();
|
||||
|
||||
public HashSet<Player> getPlayerJoinedCache() {
|
||||
return playerJoinedCache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package re.imc.geysermodelengine.managers.server;
|
||||
|
||||
import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ServerData {
|
||||
|
||||
private final ConcurrentHashMap<String, ScheduledTask> activeRunnablesCache = new ConcurrentHashMap<>();
|
||||
|
||||
public ConcurrentHashMap<String, ScheduledTask> getActiveRunnablesCache() {
|
||||
return activeRunnablesCache;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package re.imc.geysermodelengine.managers.server;
|
||||
|
||||
public class ServerManager {
|
||||
|
||||
private final ServerData serverData;
|
||||
|
||||
public ServerManager() {
|
||||
this.serverData = new ServerData();
|
||||
}
|
||||
|
||||
public ServerData getServerData() {
|
||||
return serverData;
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package re.imc.geysermodelengine.model;
|
||||
|
||||
import com.ticxo.modelengine.api.ModelEngineAPI;
|
||||
import com.ticxo.modelengine.api.entity.BukkitEntity;
|
||||
import com.ticxo.modelengine.api.model.ActiveModel;
|
||||
import com.ticxo.modelengine.api.model.bone.type.Mount;
|
||||
import com.ticxo.modelengine.api.mount.controller.MountController;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.geysermc.floodgate.api.FloodgateApi;
|
||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||
|
||||
public class BedrockMountControl {
|
||||
|
||||
public static void startTask() {
|
||||
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!FloodgateApi.getInstance().isFloodgatePlayer(player.getUniqueId())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float pitch = player.getLocation().getPitch();
|
||||
Pair<ActiveModel, Mount> seat = GeyserModelEngine.getInstance().getDrivers().get(player);
|
||||
if (seat != null) {
|
||||
if (pitch < -30) {
|
||||
MountController controller = ModelEngineAPI.getMountPairManager()
|
||||
.getController(player.getUniqueId());
|
||||
if (controller != null) {
|
||||
MountController.MountInput input = controller.getInput();
|
||||
if (input != null) {
|
||||
input.setJump(true);
|
||||
controller.setInput(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pitch > 80) {
|
||||
if (seat.getKey().getModeledEntity().getBase() instanceof BukkitEntity bukkitEntity) {
|
||||
if (bukkitEntity.getOriginal().isOnGround()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
MountController controller = ModelEngineAPI.getMountPairManager()
|
||||
.getController(player.getUniqueId());
|
||||
|
||||
if (controller != null) {
|
||||
MountController.MountInput input = controller.getInput();
|
||||
if (input != null) {
|
||||
input.setSneak(true);
|
||||
controller.setInput(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}.runTaskTimerAsynchronously(GeyserModelEngine.getInstance(), 1, 1);
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,406 +0,0 @@
|
||||
package re.imc.geysermodelengine.model;
|
||||
|
||||
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 com.ticxo.modelengine.api.model.render.DisplayRenderer;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
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.Vector3f;
|
||||
import org.joml.Vector3fc;
|
||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||
import re.imc.geysermodelengine.packet.entity.PacketEntity;
|
||||
import re.imc.geysermodelengine.util.BooleanPacker;
|
||||
|
||||
import java.awt.*;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static re.imc.geysermodelengine.model.ModelEntity.ENTITIES;
|
||||
import static re.imc.geysermodelengine.model.ModelEntity.MODEL_ENTITIES;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class EntityTask {
|
||||
public static final Method GET_SCALE;
|
||||
|
||||
static {
|
||||
try {
|
||||
GET_SCALE = ActiveModel.class.getMethod("getScale");
|
||||
} catch (NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
ModelEntity model;
|
||||
|
||||
int tick = 0;
|
||||
int syncTick = 0;
|
||||
|
||||
boolean removed = false;
|
||||
|
||||
float lastScale = -1.0f;
|
||||
Color lastColor = null;
|
||||
Map<String, Integer> lastIntSet = new ConcurrentHashMap<>();
|
||||
Cache<String, Boolean> lastPlayedAnim = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(30, TimeUnit.MILLISECONDS).build();
|
||||
|
||||
private ScheduledFuture scheduledFuture;
|
||||
|
||||
public EntityTask(ModelEntity model) {
|
||||
this.model = model;
|
||||
}
|
||||
public void runAsync() {
|
||||
PacketEntity entity = model.getEntity();
|
||||
if (entity.isDead()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PacketEntity packetEntity = model.getEntity();
|
||||
// packetEntity.setHeadYaw((float) Math.toDegrees(model.getModeledEntity().getYHeadRot()));
|
||||
// packetEntity.setHeadPitch((float) Math.toDegrees(model.getModeledEntity().getXHeadRot()));
|
||||
model.teleportToModel();
|
||||
|
||||
Set<Player> viewers = model.getViewers();
|
||||
ActiveModel activeModel = model.getActiveModel();
|
||||
ModeledEntity modeledEntity = model.getModeledEntity();
|
||||
if (activeModel.isDestroyed() || activeModel.isRemoved()) {
|
||||
removed = true;
|
||||
entity.remove();
|
||||
|
||||
ENTITIES.remove(modeledEntity.getBase().getEntityId());
|
||||
MODEL_ENTITIES.remove(entity.getEntityId());
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (tick % 5 == 0) {
|
||||
if (tick % 40 == 0) {
|
||||
for (Player viewer : Set.copyOf(viewers)) {
|
||||
if (!canSee(viewer, model.getEntity())) {
|
||||
viewers.remove(viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tick ++;
|
||||
if (tick > 400) {
|
||||
tick = 0;
|
||||
sendHitBoxToAll();
|
||||
}
|
||||
|
||||
// Optional<Player> player = viewers.stream().findAny();
|
||||
// if (player.isEmpty()) return
|
||||
|
||||
if (viewers.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// updateEntityProperties(viewers, false);
|
||||
|
||||
// do not actually use this, atleast bundle these up ;(
|
||||
sendScale(viewers, false);
|
||||
sendColor(viewers, false);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void checkViewers(Set<Player> viewers) {
|
||||
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
|
||||
if (FloodgateApi.getInstance().isFloodgatePlayer(onlinePlayer.getUniqueId())) {
|
||||
|
||||
if (canSee(onlinePlayer, model.getEntity())) {
|
||||
|
||||
if (!viewers.contains(onlinePlayer)) {
|
||||
sendSpawnPacket(onlinePlayer);
|
||||
viewers.add(onlinePlayer);
|
||||
}
|
||||
} else {
|
||||
if (viewers.contains(onlinePlayer)) {
|
||||
model.getEntity().sendEntityDestroyPacket(Collections.singletonList(onlinePlayer));
|
||||
viewers.remove(onlinePlayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void sendSpawnPacket(Player onlinePlayer) {
|
||||
EntityTask task = model.getTask();
|
||||
boolean firstJoined = !GeyserModelEngine.getInstance().getJoinedPlayers().contains(onlinePlayer);
|
||||
|
||||
if (firstJoined) {
|
||||
task.sendEntityData(onlinePlayer, GeyserModelEngine.getInstance().getJoinSendDelay() / 50);
|
||||
} else {
|
||||
task.sendEntityData(onlinePlayer, 5);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendEntityData(Player player, int delay) {
|
||||
EntityUtils.setCustomEntity(player, model.getEntity().getEntityId(), "modelengine:" + model.getActiveModel().getBlueprint().getName().toLowerCase());
|
||||
GeyserModelEngine.getInstance().getScheduler().schedule(() -> {
|
||||
model.getEntity().sendSpawnPacket(Collections.singletonList(player));
|
||||
GeyserModelEngine.getInstance().getScheduler().schedule(() -> {
|
||||
sendHitBox(player);
|
||||
sendScale(Collections.singleton(player), true);
|
||||
sendColor(Collections.singleton(player), true);
|
||||
updateEntityProperties(Collections.singleton(player), true);
|
||||
}, 500, TimeUnit.MILLISECONDS);
|
||||
}, delay * 50L, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void sendScale(Collection<Player> players, boolean firstSend) {
|
||||
try {
|
||||
if (players.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Vector3fc scale = (Vector3fc) GET_SCALE.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);
|
||||
}
|
||||
lastScale = average;
|
||||
} catch (Throwable t) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
public void sendColor(Collection<Player> players, 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);
|
||||
}
|
||||
lastColor = color;
|
||||
}
|
||||
|
||||
|
||||
public void updateEntityProperties(Collection<Player> players, boolean firstSend, String... forceAnims) {
|
||||
int entity = model.getEntity().getEntityId();
|
||||
Set<String> forceAnimSet = Set.of(forceAnims);
|
||||
|
||||
Map<String, Boolean> boneUpdates = new HashMap<>();
|
||||
Map<String, Boolean> animUpdates = new HashMap<>();
|
||||
Set<String> anims = new HashSet<>();
|
||||
// if (GeyserModelEngine.getInstance().getEnablePartVisibilityModels().contains(model.getActiveModel().getBlueprint().getName())) {
|
||||
|
||||
model.getActiveModel().getBlueprint().getBones().forEach((s, bone) -> {
|
||||
processBone(bone, boneUpdates);
|
||||
});
|
||||
// }
|
||||
|
||||
|
||||
AnimationHandler handler = model.getActiveModel().getAnimationHandler();
|
||||
Set<String> 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<String> lastPlayed = new HashSet<>(lastPlayedAnim.asMap().keySet());
|
||||
|
||||
for (Map.Entry<String, Boolean> 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<String, Integer> intUpdates = new HashMap<>();
|
||||
int i = 0;
|
||||
for (Integer integer : BooleanPacker.mapBooleansToInts(boneUpdates)) {
|
||||
intUpdates.put("modelengine:bone" + i, integer);
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
for (Integer integer : BooleanPacker.mapBooleansToInts(animUpdates)) {
|
||||
intUpdates.put("modelengine:anim" + i, integer);
|
||||
i++;
|
||||
}
|
||||
if (!firstSend) {
|
||||
if (intUpdates.equals(lastIntSet)) {
|
||||
return;
|
||||
} else {
|
||||
lastIntSet.clear();
|
||||
lastIntSet.putAll(intUpdates);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// System.out.println("AN: " + animUpdates.size() + ", BO:" + boneUpdates.size());
|
||||
if (GeyserModelEngine.getInstance().isDebug()) {
|
||||
GeyserModelEngine.getInstance().getLogger().info(animUpdates.toString());
|
||||
}
|
||||
|
||||
|
||||
List<String> list = new ArrayList<>(boneUpdates.keySet());
|
||||
Collections.sort(list);
|
||||
|
||||
for (Player player : players) {
|
||||
EntityUtils.sendIntProperties(player, entity, intUpdates);
|
||||
}
|
||||
}
|
||||
|
||||
private void processBone(BlueprintBone bone, Map<String, Boolean> 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(blueprintBone, map);
|
||||
}
|
||||
ModelBone activeBone = model.getActiveModel().getBones().get(bone.getName());
|
||||
|
||||
boolean visible = false;
|
||||
if (activeBone != null) {
|
||||
visible = activeBone.isVisible();
|
||||
}
|
||||
map.put(name, visible);
|
||||
}
|
||||
private 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 sendHitBoxToAll() {
|
||||
for (Player viewer : model.getViewers()) {
|
||||
EntityUtils.sendCustomHitBox(viewer, model.getEntity().getEntityId(), 0.01f, 0.01f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void sendHitBox(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(String animation) {
|
||||
ActiveModel activeModel = model.getActiveModel();
|
||||
BlueprintAnimation animationProperty = activeModel.getBlueprint().getAnimations().get(animation);
|
||||
return !(animationProperty == null);
|
||||
}
|
||||
|
||||
|
||||
private boolean canSee(Player player, PacketEntity entity) {
|
||||
if (!player.isOnline()) {
|
||||
return false;
|
||||
}
|
||||
if (!GeyserModelEngine.getInstance().getJoinedPlayers().contains(player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Location playerLocation = player.getLocation().clone();
|
||||
Location entityLocation = entity.getLocation().clone();
|
||||
playerLocation.setY(0);
|
||||
entityLocation.setY(0);
|
||||
if (playerLocation.getWorld() != entityLocation.getWorld()) {
|
||||
return false;
|
||||
}
|
||||
if (playerLocation.distanceSquared(entityLocation) > player.getSendViewDistance() * player.getSendViewDistance() * 48) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
/*
|
||||
if (entity.getLocation().getChunk() == player.getChunk()) {
|
||||
return true;
|
||||
}
|
||||
if (entity.getLocation().getWorld() != player.getWorld()) {
|
||||
return false;
|
||||
}
|
||||
if (player.getLocation().distanceSquared(entity.getLocation()) > player.getSimulationDistance() * player.getSimulationDistance() * 256) {
|
||||
return false;
|
||||
}
|
||||
if (player.getLocation().distance(entity.getLocation()) > model.getActiveModel().getModeledEntity().getBase().getRenderRadius()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
// syncTask.cancel();
|
||||
scheduledFuture.cancel(true);
|
||||
}
|
||||
|
||||
public void run(GeyserModelEngine instance) {
|
||||
|
||||
sendHitBoxToAll();
|
||||
|
||||
Runnable asyncTask = () -> {
|
||||
try {
|
||||
checkViewers(model.getViewers());
|
||||
runAsync();
|
||||
} catch (Throwable t) {
|
||||
|
||||
}
|
||||
};
|
||||
scheduledFuture = GeyserModelEngine.getInstance().getScheduler().scheduleAtFixedRate(asyncTask, 0, 20, TimeUnit.MILLISECONDS);
|
||||
|
||||
//asyncTask.runTaskTimerAsynchronously(instance, 0, 0);
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package re.imc.geysermodelengine.model;
|
||||
|
||||
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.ticxo.modelengine.api.model.ActiveModel;
|
||||
import com.ticxo.modelengine.api.model.ModeledEntity;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import re.imc.geysermodelengine.GeyserModelEngine;
|
||||
import re.imc.geysermodelengine.packet.entity.PacketEntity;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Getter
|
||||
public class ModelEntity {
|
||||
|
||||
public static Map<Integer, Map<ActiveModel, ModelEntity>> ENTITIES = new ConcurrentHashMap<>();
|
||||
|
||||
public static Map<Integer, ModelEntity> MODEL_ENTITIES = new ConcurrentHashMap<>();
|
||||
|
||||
private PacketEntity entity;
|
||||
|
||||
private final Set<Player> viewers = Sets.newConcurrentHashSet();
|
||||
|
||||
private final ModeledEntity modeledEntity;
|
||||
|
||||
private final ActiveModel activeModel;
|
||||
|
||||
private EntityTask task;
|
||||
|
||||
private ModelEntity(ModeledEntity modeledEntity, ActiveModel model) {
|
||||
this.modeledEntity = modeledEntity;
|
||||
this.activeModel = model;
|
||||
this.entity = spawnEntity();
|
||||
runEntityTask();
|
||||
}
|
||||
|
||||
public void teleportToModel() {
|
||||
Location location = modeledEntity.getBase().getLocation();
|
||||
entity.teleport(location);
|
||||
}
|
||||
public static ModelEntity create(ModeledEntity entity, ActiveModel model) {
|
||||
ModelEntity modelEntity = new ModelEntity(entity, model);
|
||||
int id = entity.getBase().getEntityId();
|
||||
Map<ActiveModel, ModelEntity> map = ENTITIES.computeIfAbsent(id, k -> new HashMap<>());
|
||||
for (Map.Entry<ActiveModel, ModelEntity> entry : map.entrySet()) {
|
||||
if (entry.getKey() != model && entry.getKey().getBlueprint().getName().equals(model.getBlueprint().getName())) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
map.put(model, modelEntity);
|
||||
return modelEntity;
|
||||
}
|
||||
|
||||
public PacketEntity spawnEntity() {
|
||||
entity = new PacketEntity(EntityTypes.PIG, viewers, modeledEntity.getBase().getLocation());
|
||||
return entity;
|
||||
}
|
||||
|
||||
public void runEntityTask() {
|
||||
task = new EntityTask(this);
|
||||
task.run(GeyserModelEngine.getInstance());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -27,14 +27,6 @@ import java.util.concurrent.ThreadLocalRandom;
|
||||
@Setter
|
||||
public class PacketEntity {
|
||||
|
||||
public PacketEntity(EntityType type, Set<Player> viewers, Location location) {
|
||||
this.id = ThreadLocalRandom.current().nextInt(300000000, 400000000);
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.type = type;
|
||||
this.viewers = viewers;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
private int id;
|
||||
private UUID uuid;
|
||||
private EntityType type;
|
||||
@@ -44,6 +36,15 @@ public class PacketEntity {
|
||||
private float headPitch;
|
||||
|
||||
private boolean removed = false;
|
||||
|
||||
public PacketEntity(EntityType type, Set<Player> viewers, Location location) {
|
||||
this.id = ThreadLocalRandom.current().nextInt(300000000, 400000000);
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.type = type;
|
||||
this.viewers = viewers;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
public @NotNull Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
@@ -51,10 +52,9 @@ public class PacketEntity {
|
||||
public boolean teleport(@NotNull Location location) {
|
||||
boolean sent = this.location.getWorld() != location.getWorld() || this.location.distanceSquared(location) > 0.000001;
|
||||
this.location = location.clone();
|
||||
if (sent) {
|
||||
sendLocationPacket(viewers);
|
||||
// sendHeadRotation(viewers); // TODO
|
||||
}
|
||||
|
||||
if (sent) sendLocationPacket(viewers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -73,8 +73,6 @@ public class PacketEntity {
|
||||
}
|
||||
|
||||
public void sendSpawnPacket(Collection<Player> players) {
|
||||
// EntitySpawnPacket packet = new EntitySpawnPacket(id, uuid, type, location);
|
||||
// EntityMetadataPacket metadataPacket = new EntityMetadataPacket(id);
|
||||
WrapperPlayServerSpawnEntity spawnEntity = new WrapperPlayServerSpawnEntity(id, uuid, type, SpigotConversionUtil.fromBukkitLocation(location), location.getYaw(), 0, null);
|
||||
players.forEach(player -> PacketEvents.getAPI().getPlayerManager().sendPacket(player, spawnEntity));
|
||||
}
|
||||
@@ -83,13 +81,14 @@ public class PacketEntity {
|
||||
|
||||
PacketWrapper<?> packet;
|
||||
EntityPositionData data = new EntityPositionData(SpigotConversionUtil.fromBukkitLocation(location).getPosition(), Vector3d.zero(), location.getYaw(), location.getPitch());
|
||||
|
||||
if (PacketEvents.getAPI().getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_21_2)) {
|
||||
packet = new WrapperPlayServerEntityPositionSync(id, data, false);
|
||||
} else {
|
||||
packet = new WrapperPlayServerEntityTeleport(id, data, RelativeFlag.NONE,false);
|
||||
}
|
||||
players.forEach(player -> PacketEvents.getAPI().getPlayerManager().sendPacket(player, packet));
|
||||
|
||||
players.forEach(player -> PacketEvents.getAPI().getPlayerManager().sendPacket(player, packet));
|
||||
}
|
||||
|
||||
public void sendHeadRotation(Collection<Player> players) {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package re.imc.geysermodelengine.runnables;
|
||||
|
||||
import com.ticxo.modelengine.api.ModelEngineAPI;
|
||||
import com.ticxo.modelengine.api.entity.BukkitEntity;
|
||||
import com.ticxo.modelengine.api.model.ActiveModel;
|
||||
import com.ticxo.modelengine.api.model.bone.type.Mount;
|
||||
import com.ticxo.modelengine.api.mount.controller.MountController;
|
||||
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.function.Consumer;
|
||||
|
||||
public class BedrockMountControlRunnable implements Consumer<ScheduledTask> {
|
||||
|
||||
private final GeyserModelEngine plugin;
|
||||
|
||||
public BedrockMountControlRunnable(GeyserModelEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(ScheduledTask scheduledTask) {
|
||||
for (Player player : Bukkit.getOnlinePlayers()) {
|
||||
if (!FloodgateApi.getInstance().isFloodgatePlayer(player.getUniqueId())) continue;
|
||||
|
||||
float pitch = player.getLocation().getPitch();
|
||||
Pair<ActiveModel, Mount> seat = plugin.getBedrockMountControlManager().getDriversCache().get(player);
|
||||
|
||||
if (seat == null) continue;
|
||||
|
||||
if (pitch < -30) {
|
||||
MountController controller = ModelEngineAPI.getMountPairManager()
|
||||
.getController(player.getUniqueId());
|
||||
if (controller != null) {
|
||||
MountController.MountInput input = controller.getInput();
|
||||
if (input != null) {
|
||||
input.setJump(true);
|
||||
controller.setInput(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pitch > 80) {
|
||||
if (seat.getKey().getModeledEntity().getBase() instanceof BukkitEntity bukkitEntity) {
|
||||
if (bukkitEntity.getOriginal().isOnGround()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
MountController controller = ModelEngineAPI.getMountPairManager().getController(player.getUniqueId());
|
||||
|
||||
if (controller != null) {
|
||||
MountController.MountInput input = controller.getInput();
|
||||
if (input != null) {
|
||||
input.setSneak(true);
|
||||
controller.setInput(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
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 io.papermc.paper.threadedregions.scheduler.ScheduledTask;
|
||||
import me.zimzaza4.geyserutils.spigot.api.EntityUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
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.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class EntityTaskRunnable implements Consumer<ScheduledTask> {
|
||||
|
||||
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<String, Integer> lastIntSet = new ConcurrentHashMap<>();
|
||||
private final Cache<String, Boolean> lastPlayedAnim = CacheBuilder.newBuilder().expireAfterWrite(30, TimeUnit.MILLISECONDS).build();
|
||||
|
||||
private final BooleanPacker booleanPacker = new BooleanPacker();
|
||||
|
||||
public EntityTaskRunnable(GeyserModelEngine plugin, ModelEntityData model) {
|
||||
this.plugin = plugin;
|
||||
|
||||
this.model = model;
|
||||
|
||||
plugin.getEntityTaskManager().sendHitBoxToAll(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(ScheduledTask scheduledTask) {
|
||||
plugin.getEntityTaskManager().checkViewers(model, model.getViewers());
|
||||
|
||||
PacketEntity entity = model.getEntity();
|
||||
if (entity.isDead()) return;
|
||||
|
||||
model.teleportToModel();
|
||||
|
||||
Set<Player> 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());
|
||||
scheduledTask.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 sendEntityData(ModelEntityData model, Player player, int delay) {
|
||||
EntityUtils.setCustomEntity(player, model.getEntity().getEntityId(), "modelengine:" + model.getActiveModel().getBlueprint().getName().toLowerCase());
|
||||
|
||||
Bukkit.getAsyncScheduler().runDelayed(plugin, scheduledTask -> {
|
||||
model.getEntity().sendSpawnPacket(Collections.singletonList(player));
|
||||
|
||||
Bukkit.getAsyncScheduler().runDelayed(plugin, scheduledTask1 -> {
|
||||
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<Player> players, boolean firstSend, String... forceAnims) {
|
||||
int entity = model.getEntity().getEntityId();
|
||||
Set<String> forceAnimSet = Set.of(forceAnims);
|
||||
|
||||
Map<String, Boolean> boneUpdates = new HashMap<>();
|
||||
Map<String, Boolean> animUpdates = new HashMap<>();
|
||||
Set<String> anims = new HashSet<>();
|
||||
|
||||
model.getActiveModel().getBlueprint().getBones().forEach((s, bone) -> processBone(model, bone, boneUpdates));
|
||||
|
||||
AnimationHandler handler = model.getActiveModel().getAnimationHandler();
|
||||
Set<String> 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<String> lastPlayed = new HashSet<>(lastPlayedAnim.asMap().keySet());
|
||||
|
||||
for (Map.Entry<String, Boolean> 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<String, Integer> intUpdates = new HashMap<>();
|
||||
int i = 0;
|
||||
for (Integer integer : booleanPacker.mapBooleansToInts(boneUpdates)) {
|
||||
intUpdates.put("modelengine:bone" + i, integer);
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (Integer integer : booleanPacker.mapBooleansToInts(animUpdates)) {
|
||||
intUpdates.put("modelengine: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<String> 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<String, Boolean> 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<String, Integer> getLastIntSet() {
|
||||
return lastIntSet;
|
||||
}
|
||||
|
||||
public Cache<String, Boolean> getLastPlayedAnim() {
|
||||
return lastPlayedAnim;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
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 java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class UpdateTaskRunnable implements Consumer<ScheduledTask> {
|
||||
|
||||
private final GeyserModelEngine plugin;
|
||||
|
||||
public UpdateTaskRunnable(GeyserModelEngine plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(ScheduledTask scheduledTask) {
|
||||
try {
|
||||
for (Map<ActiveModel, ModelEntityData> models : plugin.getModelManager().getEntitiesCache().values()) {
|
||||
models.values().forEach(model -> model.getEntityTask().updateEntityProperties(model, model.getViewers(), false));
|
||||
}
|
||||
} catch (Throwable err) {
|
||||
throw new RuntimeException(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,25 +6,30 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class BooleanPacker {
|
||||
public static final int MAX_BOOLEANS = 24;
|
||||
|
||||
public static int booleansToInt(List<Boolean> booleans) {
|
||||
private final int MAX_BOOLEANS = 24;
|
||||
|
||||
public int booleansToInt(List<Boolean> booleans) {
|
||||
int result = 0;
|
||||
int i = 1;
|
||||
|
||||
for (boolean b : booleans) {
|
||||
if (b) {
|
||||
result += i;
|
||||
}
|
||||
i *= 2;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int mapBooleansToInt(Map<String, Boolean> booleanMap) {
|
||||
public int mapBooleansToInt(Map<String, Boolean> booleanMap) {
|
||||
int result = 0;
|
||||
int i = 1;
|
||||
|
||||
List<String> keys = new ArrayList<>(booleanMap.keySet());
|
||||
Collections.sort(keys);
|
||||
|
||||
for (String key : keys) {
|
||||
if (booleanMap.get(key)) {
|
||||
result += i;
|
||||
@@ -34,11 +39,12 @@ public class BooleanPacker {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<Integer> booleansToInts(List<Boolean> booleans) {
|
||||
public List<Integer> booleansToInts(List<Boolean> booleans) {
|
||||
List<Integer> results = new ArrayList<>();
|
||||
int result = 0;
|
||||
int i = 1;
|
||||
int i1 = 1;
|
||||
|
||||
for (boolean b : booleans) {
|
||||
if (b) {
|
||||
result += i;
|
||||
@@ -56,15 +62,15 @@ public class BooleanPacker {
|
||||
return results;
|
||||
}
|
||||
|
||||
public static List<Integer> mapBooleansToInts(Map<String, Boolean> booleanMap) {
|
||||
public List<Integer> mapBooleansToInts(Map<String, Boolean> booleanMap) {
|
||||
List<String> keys = new ArrayList<>(booleanMap.keySet());
|
||||
List<Boolean> booleans = new ArrayList<>();
|
||||
|
||||
Collections.sort(keys);
|
||||
|
||||
for (String key : keys) {
|
||||
booleans.add(booleanMap.get(key));
|
||||
}
|
||||
return booleansToInts(booleans);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
29
src/main/java/re/imc/geysermodelengine/util/ColourUtils.java
Normal file
29
src/main/java/re/imc/geysermodelengine/util/ColourUtils.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package re.imc.geysermodelengine.util;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
|
||||
import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ColourUtils {
|
||||
|
||||
private final MiniMessage miniMessage = MiniMessage.miniMessage();
|
||||
|
||||
public @NotNull Component miniFormat(String message) {
|
||||
return miniMessage.deserialize(message).decoration(TextDecoration.ITALIC, false);
|
||||
}
|
||||
|
||||
public @NotNull Component miniFormat(String message, TagResolver tagResolver) {
|
||||
return miniMessage.deserialize(message, tagResolver).decoration(TextDecoration.ITALIC, false);
|
||||
}
|
||||
|
||||
public String stripColour(Component component) {
|
||||
return PlainTextComponentSerializer.plainText().serialize(component);
|
||||
}
|
||||
|
||||
public MiniMessage getMiniMessage() {
|
||||
return miniMessage;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user