Initial commit

This commit is contained in:
zimzaza4
2024-04-13 11:30:22 +08:00
commit 26ff044a59
20 changed files with 1183 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
# 项目排除路径
/target/

3
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
# 默认忽略的文件
/shelf/
/workspace.xml

16
.idea/checkstyle-idea.xml generated Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CheckStyle-IDEA" serialisationVersion="2">
<checkstyleVersion>10.12.3</checkstyleVersion>
<scanScope>JavaOnly</scanScope>
<copyLibs>true</copyLibs>
<option name="thirdPartyClasspath" />
<option name="activeLocationIds" />
<option name="locations">
<list>
<ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">(bundled)</ConfigurationLocation>
<ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">(bundled)</ConfigurationLocation>
</list>
</option>
</component>
</project>

13
.idea/compiler.xml generated Normal file
View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile name="Maven default annotation processors profile" enabled="true">
<sourceOutputDir name="target/generated-sources/annotations" />
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
<outputRelativeToContentRoot value="true" />
<module name="GeyserModelEngine" />
</profile>
</annotationProcessing>
</component>
</project>

7
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding">
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
</component>
</project>

55
.idea/jarRepositories.xml generated Normal file
View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Central Repository" />
<option name="url" value="https://repo.maven.apache.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="opencollab-snapshot-repo" />
<option name="name" value="opencollab-snapshot-repo" />
<option name="url" value="https://repo.opencollab.dev/maven-snapshots/" />
</remote-repository>
<remote-repository>
<option name="id" value="sonatype" />
<option name="name" value="sonatype" />
<option name="url" value="https://oss.sonatype.org/content/groups/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="opencollab-release-repo" />
<option name="name" value="opencollab-release-repo" />
<option name="url" value="https://repo.opencollab.dev/maven-releases/" />
</remote-repository>
<remote-repository>
<option name="id" value="dmulloy2-repo" />
<option name="name" value="dmulloy2-repo" />
<option name="url" value="https://repo.dmulloy2.net/repository/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="papermc-repo" />
<option name="name" value="papermc-repo" />
<option name="url" value="https://repo.papermc.io/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="nexus" />
<option name="name" value="Lumine Public" />
<option name="url" value="https://mvn.lumine.io/repository/maven-public/" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="md_5-public" />
<option name="name" value="md_5-public" />
<option name="url" value="https://repo.md-5.net/content/groups/public/" />
</remote-repository>
</component>
</project>

19
.idea/misc.xml generated Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="EntryPointsManager">
<list size="1">
<item index="0" class="java.lang.String" itemvalue="org.bukkit.event.EventHandler" />
</list>
</component>
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="MavenProjectsManager">
<option name="originalFiles">
<list>
<option value="$PROJECT_DIR$/pom.xml" />
</list>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/GeyserModelEngine.iml" filepath="$PROJECT_DIR$/GeyserModelEngine.iml" />
</modules>
</component>
</project>

124
.idea/uiDesigner.xml generated Normal file
View File

@@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

16
GeyserModelEngine.iml Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module version="4">
<component name="FacetManager">
<facet type="minecraft" name="Minecraft">
<configuration>
<autoDetectTypes>
<platformType>PAPER</platformType>
<platformType>ADVENTURE</platformType>
</autoDetectTypes>
</configuration>
</facet>
</component>
<component name="McpModuleSettings">
<option name="srgType" value="SRG" />
</component>
</module>

Binary file not shown.

138
pom.xml Normal file
View File

@@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>re.imc</groupId>
<artifactId>GeyserModelEngine</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>GeyserModelEngine</name>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>16</source>
<target>16</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
<repositories>
<repository>
<id>papermc-repo</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
<repository>
<id>sonatype</id>
<url>https://oss.sonatype.org/content/groups/public/</url>
</repository>
<repository>
<id>nexus</id>
<name>Lumine Public</name>
<url>https://mvn.lumine.io/repository/maven-public/</url>
</repository>
<repository>
<id>md_5-public</id>
<url>https://repo.md-5.net/content/groups/public/</url>
</repository>
<repository>
<id>opencollab-release-repo</id>
<url>https://repo.opencollab.dev/maven-releases/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>opencollab-snapshot-repo</id>
<url>https://repo.opencollab.dev/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>dmulloy2-repo</id>
<url>https://repo.dmulloy2.net/repository/public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.20.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.ticxo.modelengine</groupId>
<artifactId>ModelEngine</artifactId>
<version>R4.0.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>me.zimzaza4</groupId>
<artifactId>geyserutils-spigot</artifactId>
<version>1.0.1</version>
<scope>system</scope>
<systemPath>${project.basedir}/libs/geyserutils-spigot-1.0-SNAPSHOT.jar</systemPath>
</dependency>
<dependency>
<groupId>org.geysermc.floodgate</groupId>
<artifactId>api</artifactId>
<version>2.2.2-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>LibsDisguises</groupId>
<artifactId>LibsDisguises</artifactId>
<version>10.0.35</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.comphenix.protocol</groupId>
<artifactId>ProtocolLib</artifactId>
<version>5.1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,89 @@
package re.imc.geysermodelengine;
import com.comphenix.protocol.ProtocolLibrary;
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 lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import re.imc.geysermodelengine.listener.AddEntityPacketListener;
import re.imc.geysermodelengine.listener.InteractPacketListener;
import re.imc.geysermodelengine.listener.ModelListener;
import re.imc.geysermodelengine.model.ModelEntity;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
public final class GeyserModelEngine extends JavaPlugin {
@Getter
private static GeyserModelEngine instance;
@Getter
private static boolean alwaysSendSkin;
@Getter
private int skinSendDelay;
@Getter
private int viewDistance;
@Getter
private EntityType modelEntityType;
@Getter
private Cache<Player, Boolean> joinedPlayer;
@Override
public void onEnable() {
// Plugin startup logic
saveDefaultConfig();
// alwaysSendSkin = getConfig().getBoolean("always-send-skin");
skinSendDelay = getConfig().getInt("skin-send-delay");
viewDistance = getConfig().getInt("skin-view-distance");
modelEntityType = EntityType.valueOf(getConfig().getString("model-entity-type", "BAT"));
int joinedDelay = getConfig().getInt("join-send-delay");
if (joinedDelay > 0) {
joinedPlayer = CacheBuilder.newBuilder()
.expireAfterWrite(joinedDelay * 50L, TimeUnit.MILLISECONDS).build();
}
instance = this;
ProtocolLibrary.getProtocolManager().addPacketListener(new InteractPacketListener());
ProtocolLibrary.getProtocolManager().addPacketListener(new AddEntityPacketListener());
Bukkit.getPluginManager().registerEvents(new ModelListener(), this);
Bukkit.getScheduler()
.runTaskLater(GeyserModelEngine.getInstance(), () -> {
for (World world : Bukkit.getWorlds()) {
for (Entity entity : world.getEntities()) {
if (!ModelEntity.ENTITIES.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));
}
}
}
}
}, 100);
}
@Override
public void onDisable() {
for (Map<ActiveModel, ModelEntity> entities : ModelEntity.ENTITIES.values()) {
entities.forEach((model, modelEntity) -> {
modelEntity.getEntity().remove();
});
}
// Plugin shutdown logic
}
}

View File

@@ -0,0 +1,34 @@
package re.imc.geysermodelengine.listener;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.*;
import com.comphenix.protocol.reflect.StructureModifier;
import com.ticxo.modelengine.api.entity.BukkitEntity;
import org.bukkit.entity.Entity;
import org.geysermc.floodgate.api.FloodgateApi;
import org.jetbrains.annotations.NotNull;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.model.ModelEntity;
import java.util.Set;
public class AddEntityPacketListener extends PacketAdapter {
public AddEntityPacketListener() {
super(GeyserModelEngine.getInstance(), ListenerPriority.HIGHEST, Set.of(PacketType.Play.Server.SPAWN_ENTITY), ListenerOptions.SYNC);
}
@Override
public void onPacketSending(PacketEvent event) {
PacketContainer packet = event.getPacket();
StructureModifier<Entity> modifier = packet.getEntityModifier(event);
Entity entity = modifier.readSafely(0);
if (entity == null) {
return;
}
ModelEntity model = ModelEntity.MODEL_ENTITIES.get(entity.getEntityId());
if (model != null && FloodgateApi.getInstance().isFloodgatePlayer(event.getPlayer().getUniqueId())) {
model.getTask().sendEntityData(event.getPlayer(), GeyserModelEngine.getInstance().getSkinSendDelay());
}
}
}

View File

@@ -0,0 +1,39 @@
package re.imc.geysermodelengine.listener;
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.events.*;
import com.comphenix.protocol.reflect.StructureModifier;
import com.ticxo.modelengine.api.entity.BukkitEntity;
import com.ticxo.modelengine.api.entity.BukkitPlayer;
import org.bukkit.entity.Entity;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.model.ModelEntity;
import java.util.Set;
public class InteractPacketListener extends PacketAdapter {
public InteractPacketListener() {
super(GeyserModelEngine.getInstance(), ListenerPriority.HIGHEST, Set.of(PacketType.Play.Client.USE_ENTITY), ListenerOptions.SYNC);
}
@Override
public void onPacketReceiving(PacketEvent event) {
PacketContainer packet = event.getPacket();
StructureModifier<Entity> modifier = packet.getEntityModifier(event);
Entity entity = modifier.readSafely(0);
if (entity == null) {
return;
}
ModelEntity model = ModelEntity.MODEL_ENTITIES.get(entity.getEntityId());
if (model != null && model.getModeledEntity().getBase() instanceof BukkitEntity bukkitEntity) {
modifier.writeSafely(0, bukkitEntity.getOriginal());
event.setPacket(packet);
}
}
}

View File

@@ -0,0 +1,142 @@
package re.imc.geysermodelengine.listener;
import com.ticxo.modelengine.api.ModelEngineAPI;
import com.ticxo.modelengine.api.events.AddModelEvent;
import com.ticxo.modelengine.api.events.AnimationEndEvent;
import com.ticxo.modelengine.api.events.AnimationPlayEvent;
import com.ticxo.modelengine.api.events.RemoveModelEvent;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import org.bukkit.Bukkit;
import org.bukkit.entity.Damageable;
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.EntityDamageEvent;
import org.bukkit.event.entity.ProjectileHitEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.world.EntitiesLoadEvent;
import org.bukkit.metadata.FixedMetadataValue;
import re.imc.geysermodelengine.GeyserModelEngine;
import re.imc.geysermodelengine.model.EntityTask;
import re.imc.geysermodelengine.model.ModelEntity;
import java.util.Map;
import java.util.Optional;
public class ModelListener implements Listener {
@EventHandler(priority = EventPriority.MONITOR)
public void onAddModel(AddModelEvent event) {
if (event.isCancelled()) {
return;
}
Bukkit.getScheduler().runTask(GeyserModelEngine.getInstance(), () -> {
ModelEntity.create(event.getTarget(), event.getModel());
});
}
@EventHandler
public void onRemoveModel(RemoveModelEvent event) {
event.getTarget().getBase();
}
@EventHandler
public void onEntityLoad(EntitiesLoadEvent event) {
Bukkit.getScheduler()
.runTaskLater(GeyserModelEngine.getInstance(), () -> {
for (Entity entity : event.getEntities()) {
if (!ModelEntity.ENTITIES.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));
}
}
}
}, 20);
}
@EventHandler
public void onAnimationPlay(AnimationPlayEvent event) {
ModelEntity model = ModelEntity.ENTITIES.get(event.getModel().getModeledEntity().getBase().getEntityId()).get(event.getModel());
if (model != null) {
EntityTask task = model.getTask();
int p = (event.getProperty().isForceOverride() ? 80 : (event.getProperty().isOverride() ? 70 : 60));
task.playAnimation(event.getProperty().getName(), p);
}
}
@EventHandler
public void onModelHurt(EntityDamageEvent event) {
ModelEntity model = ModelEntity.MODEL_ENTITIES.get(event.getEntity().getEntityId());
if (model != null) {
if (!event.getEntity().hasMetadata("show_damage")) {
event.setCancelled(true);
}
event.getEntity().removeMetadata("show_damage", GeyserModelEngine.getInstance());
if (model.getEntity() instanceof Damageable damageable) {
event.setDamage(0);
damageable.setHealth(damageable.getMaxHealth());
}
}
}
@EventHandler
public void onModelEntityHurt(EntityDamageEvent event) {
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() instanceof Damageable damageable) {
damageable.setMetadata("show_damage", new FixedMetadataValue(GeyserModelEngine.getInstance(), true));
damageable.damage(0);
}
}
}
}
/*
@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
public void onModelHit(ProjectileHitEvent event) {
if (event.getHitEntity() == null) {
return;
}
ModelEntity model = ModelEntity.MODEL_ENTITIES.get(event.getHitEntity().getEntityId());
if (model != null) {
event.setCancelled(true);
if (model.getEntity() instanceof Damageable damageable) {
damageable.setHealth(damageable.getMaxHealth());
}
}
}
@EventHandler
public void onAnimationEnd(AnimationEndEvent event) {
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
GeyserModelEngine.getInstance().getJoinedPlayer().put(event.getPlayer(), true);
}
}

View File

@@ -0,0 +1,370 @@
package re.imc.geysermodelengine.model;
import com.ticxo.modelengine.api.animation.BlueprintAnimation;
import com.ticxo.modelengine.api.entity.BaseEntity;
import com.ticxo.modelengine.api.entity.BukkitEntity;
import com.ticxo.modelengine.api.entity.Hitbox;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import lombok.Getter;
import lombok.Setter;
import me.zimzaza4.geyserutils.common.animation.Animation;
import me.zimzaza4.geyserutils.spigot.api.PlayerUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.BoundingBox;
import org.geysermc.floodgate.api.FloodgateApi;
import org.jetbrains.annotations.NotNull;
import re.imc.geysermodelengine.GeyserModelEngine;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import static re.imc.geysermodelengine.model.ModelEntity.ENTITIES;
import static re.imc.geysermodelengine.model.ModelEntity.MODEL_ENTITIES;
@Getter
@Setter
public class EntityTask {
ModelEntity model;
int tick = 0;
AtomicInteger animationCooldown = new AtomicInteger(0);
AtomicInteger currentAnimationPriority = new AtomicInteger(0);
boolean firstAnimation = true;
boolean spawnAnimationPlayed = false;
String lastAnimation = "";
boolean looping = false;
private BukkitRunnable syncTask;
private BukkitRunnable asyncTask;
public EntityTask(ModelEntity model) {
this.model = model;
}
public void runSync() {
if (model.getEntity().isDead()) {
model.spawnEntity();
}
model.getEntity().setVisualFire(false);
model.teleportToModel();
}
public void runAsync() {
Entity entity = model.getEntity();
Set<Player> viewers = model.getViewers();
ActiveModel activeModel = model.getActiveModel();
ModeledEntity modeledEntity = model.getModeledEntity();
if (modeledEntity.isDestroyed() || !modeledEntity.getBase().isAlive()) {
if (!modeledEntity.isDestroyed() && !modeledEntity.getBase().isAlive()) {
String animation = hasAnimation("death") ? "death" : "idle";
new BukkitRunnable() {
@Override
public void run() {
entity.remove();
}
}.runTaskLater(GeyserModelEngine.getInstance(), Math.max(playAnimation(animation, 99) - 1, 0));
}
ENTITIES.remove(modeledEntity.getBase().getEntityId());
MODEL_ENTITIES.remove(entity.getEntityId());
cancel();
return;
}
if (model.getEntity().isDead()) {
ENTITIES.remove(modeledEntity.getBase().getEntityId());
MODEL_ENTITIES.remove(entity.getEntityId());
cancel();
return;
}
/*
if (waitingTick > 0) {
waitingTick--;
}
*/
if (tick > 1 && tick % 5 == 0) {
for (Player onlinePlayer : Bukkit.getOnlinePlayers()) {
if (!FloodgateApi.getInstance().isFloodgatePlayer(onlinePlayer.getUniqueId())) {
onlinePlayer.hideEntity(GeyserModelEngine.getInstance(), entity);
} else {
if (canSee(onlinePlayer, model.getEntity())) {
if (!viewers.contains(onlinePlayer)) {
viewers.add(onlinePlayer);
/*
if (GeyserModelEngine.getInstance().getSkinSendDelay() > 0) {
sendEntityData(onlinePlayer, GeyserModelEngine.getInstance().getSkinSendDelay());
} else {
PlayerUtils.sendCustomSkin(onlinePlayer, model.getEntity(), activeModel.getBlueprint().getName());
Bukkit.getScheduler().runTaskLaterAsynchronously(GeyserModelEngine.getInstance(), () -> {
sendHitBox(onlinePlayer);
}, 2);
}
*/
}
} else {
viewers.remove(onlinePlayer);
}
}
}
if (!spawnAnimationPlayed) {
spawnAnimationPlayed = true;
if (hasAnimation("spawn")) {
playAnimation("spawn", 99);
} else {
playAnimation("idle", 0);
}
}
if (tick % 40 == 0) {
for (Player viewer : Set.copyOf(viewers)) {
if (!canSee(viewer, model.getEntity())) {
viewers.remove(viewer);
}
/*
if (GeyserModelEngine.isAlwaysSendSkin()) {
PlayerUtils.sendCustomSkin(viewer, model.getEntity(), activeModel.getBlueprint().getName());
}
*/
}
}
}
tick ++;
if (tick > 400) {
tick = 0;
sendHitBoxToAll();
}
BaseEntity<?> base = modeledEntity.getBase();
if (base.isStrafing() && hasAnimation("strafe")) {
playAnimation("strafe", 50);
} else if (base.isFlying() && hasAnimation("fly")) {
playAnimation("fly", 40);
} else if (base.isJumping() && hasAnimation("jump")) {
playAnimation("jump", 30);
} else if (base.isWalking() && hasAnimation("walk")) {
playAnimation("walk", 20);
} else if (hasAnimation("idle")) {
playAnimation("idle", 0);
}
if (animationCooldown.get() > 0) {
animationCooldown.decrementAndGet();
}
}
public void sendEntityData(Player player, int delay) {
Bukkit.getScheduler().runTaskLaterAsynchronously(GeyserModelEngine.getInstance(), () -> {
PlayerUtils.sendCustomSkin(player, model.getEntity(), model.getActiveModel().getBlueprint().getName());
playBedrockAnimation("animation." + model.getActiveModel().getBlueprint().getName() + "." + lastAnimation, looping);
sendHitBox(player);
Bukkit.getScheduler().runTaskLaterAsynchronously(GeyserModelEngine.getInstance(), () -> {
sendHitBox(player);
}, 8);
}, delay);
}
public void sendHitBoxToAll() {
for (Player viewer : model.getViewers()) {
if (model.getModeledEntity().getBase() instanceof BukkitEntity bukkitEntity) {
@NotNull BoundingBox box = bukkitEntity.getOriginal().getBoundingBox();
PlayerUtils.sendCustomHitBox(viewer, model.getEntity(), (float) box.getHeight(), (float) ((box.getWidthX() + box.getWidthZ()) / 2f));
// huh i dont know how to deal with width
}
}
}
public void sendHitBox(Player viewer) {
if (model.getModeledEntity().getBase() instanceof BukkitEntity bukkitEntity) {
@NotNull BoundingBox box = bukkitEntity.getOriginal().getBoundingBox();
PlayerUtils.sendCustomHitBox(viewer, model.getEntity(), (float) box.getHeight(), (float) ((box.getWidthX() + box.getWidthZ()) / 2f));
// huh i dont know how to deal with width
}
}
public boolean hasAnimation(String animation) {
ActiveModel activeModel = model.getActiveModel();
BlueprintAnimation animationProperty = activeModel.getBlueprint().getAnimations().get(animation);
return !(animationProperty == null);
}
public int playAnimation(String animation, int p) {
ActiveModel activeModel = model.getActiveModel();
BlueprintAnimation animationProperty = activeModel.getBlueprint().getAnimations().get(animation);
if (animationProperty == null) {
return 0;
}
boolean play = false;
if (currentAnimationPriority.get() < p) {
currentAnimationPriority.set(p);
play = true;
} else if (animationCooldown.get() == 0) {
play = true;
}
boolean delaySend = false;
if (firstAnimation) {
delaySend = true;
firstAnimation = false;
}
boolean lastLoopState = looping;
looping = animationProperty.getLoopMode() == BlueprintAnimation.LoopMode.LOOP;;
if (lastAnimation.equals(animation)) {
if (looping) {
// play = waitingTick == 1;
play = false;
}
}
if (play) {
currentAnimationPriority.set(p);
if (lastLoopState && !lastAnimation.equals(animation)) {
// clearLoopAnimation();
// delaySend = true;
}
String id = "animation." + activeModel.getBlueprint().getName() + "." + animationProperty.getName();
lastAnimation = id;
animationCooldown.set((int) (animationProperty.getLength() * 20));
if (delaySend) {
Bukkit.getScheduler().runTaskLaterAsynchronously(GeyserModelEngine.getInstance(), () -> playBedrockAnimation("animation." + activeModel.getBlueprint().getName() + "." + animationProperty.getName(), looping), 2);
} else {
playBedrockAnimation(id, looping);
}
}
return animationCooldown.get();
}
/*
private void clearLoopAnimation() {
playStopBedrockAnimation(lastAnimation);
}
private void playStopBedrockAnimation(String animationId) {
Entity entity = model.getEntity();
Set<Player> viewers = model.getViewers();
// model.getViewers().forEach(viewer -> viewer.sendActionBar("CURRENT AN:" + "STOP"));
Animation.AnimationBuilder animation = Animation.builder()
.stopExpression("!query.any_animation_finished")
.animation(animationId)
.nextState(animationId)
.controller("controller.animation.armor_stand.wiggle")
.blendOutTime(0f);
for (Player viewer : viewers) {
PlayerUtils.playEntityAnimation(viewer, animation.build(), entity);
}
}
*/
private void playBedrockAnimation(String animationId, boolean loop) {
// model.getViewers().forEach(viewer -> viewer.sendActionBar("CURRENT AN:" + animationId));
Entity entity = model.getEntity();
Set<Player> viewers = model.getViewers();
Animation.AnimationBuilder animation = Animation.builder()
.animation(animationId)
.blendOutTime(0f)
.controller("controller.animation.armor_stand.wiggle");
if (loop) {
animation.nextState(animationId);
}
for (Player viewer : viewers) {
PlayerUtils.playEntityAnimation(viewer, animation.build(), entity);
}
}
private boolean canSee(Player player, Entity entity) {
if (!player.isOnline()) {
return false;
}
if (player.isDead()) {
return false;
}
if (player.getWorld() != entity.getWorld()) {
return false;
}
if (GeyserModelEngine.getInstance().getJoinedPlayer() != null && GeyserModelEngine.getInstance().getJoinedPlayer().getIfPresent(player) != null) {
return false;
}
if (entity.getChunk() == player.getChunk()) {
return true;
}
if (player.getLocation().distanceSquared(entity.getLocation()) > player.getSimulationDistance() * player.getSimulationDistance() * 256) {
return false;
}
if (player.getLocation().distance(entity.getLocation()) > GeyserModelEngine.getInstance().getViewDistance()) {
return false;
}
return true;
}
public void cancel() {
syncTask.cancel();
asyncTask.cancel();
}
public void run(GeyserModelEngine instance, int i) {
syncTask = new BukkitRunnable() {
@Override
public void run() {
runSync();
}
};
syncTask.runTaskTimer(instance, i, 0);
asyncTask = new BukkitRunnable() {
@Override
public void run() {
runAsync();
}
};
asyncTask.runTaskTimerAsynchronously(instance, i + 2, 0);
}
}

View File

@@ -0,0 +1,96 @@
package re.imc.geysermodelengine.model;
import com.comphenix.protocol.wrappers.WrappedGameProfile;
import com.google.common.collect.Sets;
import com.ticxo.modelengine.api.model.ActiveModel;
import com.ticxo.modelengine.api.model.ModeledEntity;
import lombok.Getter;
import me.libraryaddict.disguise.DisguiseAPI;
import me.libraryaddict.disguise.disguisetypes.PlayerDisguise;
import org.bukkit.entity.Bat;
import org.bukkit.entity.Entity;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import re.imc.geysermodelengine.GeyserModelEngine;
import java.util.*;
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 LivingEntity entity;
private Set<Player> viewers = Sets.newConcurrentHashSet();
private ModeledEntity modeledEntity;
private ActiveModel activeModel;
private EntityTask task;
private ModelEntity(ModeledEntity modeledEntity, ActiveModel model) {
this.modeledEntity = modeledEntity;
this.activeModel = model;
this.entity = spawnEntity();
runEntityTask();
}
public void teleportToModel() {
entity.teleportAsync(modeledEntity.getBase().getLocation());
}
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<>());
map.put(model, modelEntity);
ENTITIES.put(id, map);
return modelEntity;
}
public LivingEntity spawnEntity() {
entity = (LivingEntity) modeledEntity.getBase().getLocation().getWorld().spawnEntity(modeledEntity.getBase().getLocation(), GeyserModelEngine.getInstance().getModelEntityType());
applyFeatures(entity, "model." + activeModel.getBlueprint().getName());
ModelEntity model = this;
int id = entity.getEntityId();
MODEL_ENTITIES.put(id, model);
return entity;
}
public void runEntityTask() {
task = new EntityTask(this);
task.run(GeyserModelEngine.getInstance(), 0);
}
private void applyFeatures(LivingEntity display, String name) {
display.setGravity(false);
display.setMaxHealth(2048);
display.setHealth(2048);
//display.setInvulnerable(true);
display.setAI(false);
display.setSilent(true);
display.setPersistent(false);
// armorStand.setVisible(false);
String uuid = UUID.randomUUID().toString();
PlayerDisguise disguise = new PlayerDisguise(name + "_" + uuid);
DisguiseAPI.disguiseEntity(display, disguise.setNameVisible(false));
}
}

View File

@@ -0,0 +1,4 @@
skin-send-delay: 0
skin-view-distance: 50
join-send-delay: 10
model-entity-type: BAT # must be a living entity

View File

@@ -0,0 +1,8 @@
name: GeyserModelEngine
version: '${project.version}'
main: re.imc.geysermodelengine.GeyserModelEngine
api-version: '1.19'
depend:
- ModelEngine
- LibsDisguises
- floodgate