Skip to content

Commit

Permalink
implement most remaining stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
derklaro committed Jan 27, 2025
1 parent df33d95 commit 0dcd292
Show file tree
Hide file tree
Showing 16 changed files with 427 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Setup java
uses: actions/setup-java@v4
with:
java-version: 17
java-version: 21
check-latest: true
distribution: 'zulu'

Expand Down
2 changes: 1 addition & 1 deletion checkstyle.xml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"https://checkstyle.org/dtds/configuration_1_3.dtd">
<module name="Checker">
<module name="BeforeExecutionExclusionFileFilter">
<property name="fileNamePattern" value="module\-info\.java$"/>
<property name="fileNamePattern" value="(module\-info\.java$)|(.*[\\|\/]mixins[\\|\/].*$)"/>
</module>
<module name="SuppressionFilter">
<property default="checkstyle-suppressions.xml" name="file"
Expand Down
17 changes: 15 additions & 2 deletions fabric/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ plugins {
alias(libs.plugins.fabricLoom)
}

configurations {
// custom configuration for later dependency resolution
create("runtimeImpl") {
configurations.getByName("api").extendsFrom(this)
}
}

dependencies {
minecraft(libs.minecraft)
modImplementation(libs.fabricLoader)
Expand All @@ -34,12 +41,18 @@ dependencies {
modImplementation(platform(libs.fabricApiBom))
modImplementation(libs.fabricApiNetworkingV1)

api(projects.npcLibApi)
api(projects.npcLibCommon)
"runtimeImpl"(projects.npcLibApi)
"runtimeImpl"(projects.npcLibCommon)

implementation(libs.geantyref)
}

tasks.withType<Jar> {
dependsOn(":npc-lib-api:jar")
dependsOn(":npc-lib-common:jar")
from(configurations.getByName("runtimeImpl").map { if (it.isDirectory) it else zipTree(it) })
}

tasks.withType<JavaCompile> {
options.release.set(21)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,9 @@
package com.github.juliarn.npclib.fabric;

import net.fabricmc.api.ModInitializer;
import net.minecraft.server.MinecraftServer;

public final class FabricModInitializer implements ModInitializer {

public static MinecraftServer theServer;

@Override
public void onInitialize() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,11 @@

package com.github.juliarn.npclib.fabric;

import com.github.juliarn.npclib.api.NpcActionController;
import com.github.juliarn.npclib.api.Platform;
import com.github.juliarn.npclib.common.platform.CommonPlatform;
import com.github.juliarn.npclib.common.platform.CommonPlatformBuilder;
import com.github.juliarn.npclib.fabric.controller.FabricActionController;
import com.github.juliarn.npclib.fabric.protocol.FabricProtocolAdapter;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
Expand All @@ -34,6 +37,13 @@

public final class FabricPlatform extends CommonPlatformBuilder<ServerLevel, ServerPlayer, ItemStack, Object> {

private FabricPlatform() {
}

public static @NotNull Platform.Builder<ServerLevel, ServerPlayer, ItemStack, Object> fabricNpcPlatformBuilder() {
return new FabricPlatform();
}

@Override
protected void prepareBuild() {
// set the default task manager
Expand Down Expand Up @@ -64,6 +74,28 @@ protected void prepareBuild() {

@Override
protected @NotNull Platform<ServerLevel, ServerPlayer, ItemStack, Object> doBuild() {
return null;
// check if we need an action controller
NpcActionController actionController = null;
if (this.actionControllerDecorator != null) {
NpcActionController.Builder builder = FabricActionController.actionControllerBuilder(
this.eventManager,
this.npcTracker);
this.actionControllerDecorator.accept(builder);
actionController = builder.build();
}

// build the platform
return new CommonPlatform<>(
this.debug,
this.extension,
this.logger,
this.npcTracker,
this.profileResolver,
this.taskManager,
actionController,
this.versionAccessor,
this.eventManager,
this.worldAccessor,
this.packetAdapter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import com.github.juliarn.npclib.api.PlatformTaskManager;
import com.github.juliarn.npclib.common.task.AsyncPlatformTaskManager;
import com.github.juliarn.npclib.fabric.util.FabricUtil;
import org.jetbrains.annotations.NotNull;

public final class FabricPlatformTaskManager extends AsyncPlatformTaskManager {
Expand All @@ -42,7 +43,7 @@ private FabricPlatformTaskManager() {

@Override
public void scheduleSync(@NotNull Runnable task) {
FabricModInitializer.theServer.execute(task);
FabricUtil.getServer().execute(task);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package com.github.juliarn.npclib.fabric;

import com.github.juliarn.npclib.api.PlatformWorldAccessor;
import com.github.juliarn.npclib.fabric.util.FabricUtil;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -46,7 +47,7 @@ private static final class KeyBasedLevelAccessor implements PlatformWorldAccesso

@Override
public @Nullable ServerLevel resolveWorldFromIdentifier(@NotNull String identifier) {
var levels = FabricModInitializer.theServer.getAllLevels();
var levels = FabricUtil.getServer().getAllLevels();
for (var level : levels) {
var levelIdentifier = this.extractWorldIdentifier(level);
if (levelIdentifier.equals(identifier)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,200 @@

package com.github.juliarn.npclib.fabric.controller;

public class FabricActionController {
import com.github.juliarn.npclib.api.Npc;
import com.github.juliarn.npclib.api.NpcActionController;
import com.github.juliarn.npclib.api.NpcTracker;
import com.github.juliarn.npclib.api.event.ShowNpcEvent;
import com.github.juliarn.npclib.api.event.manager.NpcEventManager;
import com.github.juliarn.npclib.api.flag.NpcFlag;
import com.github.juliarn.npclib.api.protocol.enums.EntityAnimation;
import com.github.juliarn.npclib.api.protocol.meta.EntityMetadataFactory;
import com.github.juliarn.npclib.common.CommonNpcActionController;
import com.github.juliarn.npclib.common.flag.CommonNpcFlaggedBuilder;
import com.github.juliarn.npclib.fabric.util.FabricUtil;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@ApiStatus.Internal
public final class FabricActionController extends CommonNpcActionController {

private final NpcTracker<ServerLevel, ServerPlayer, ItemStack, Object> npcTracker;

// based on the given flags
private final int spawnDistance;
private final int imitateDistance;

public FabricActionController(
@NotNull Map<NpcFlag<?>, Optional<?>> flags,
@NotNull NpcEventManager eventManager,
@NotNull NpcTracker<ServerLevel, ServerPlayer, ItemStack, Object> tracker
) {
super(flags);
this.npcTracker = tracker;

// pre-calculate flag values
var spawnDistance = this.flagValueOrDefault(SPAWN_DISTANCE);
this.spawnDistance = spawnDistance * spawnDistance;

var imitateDistance = this.flagValueOrDefault(IMITATE_DISTANCE);
this.imitateDistance = imitateDistance * imitateDistance;

// register listener to update the npc rotation after it is tracked
if (this.flagValueOrDefault(NpcActionController.AUTO_SYNC_POSITION_ON_SPAWN)) {
eventManager.registerEventHandler(ShowNpcEvent.Post.class, event -> {
ServerPlayer player = event.player();
var pos = player.position();
var rot = player.getRotationVector();
var level = player.serverLevel();

double distance = FabricUtil.distance(event.npc(), pos);
if (distance <= this.imitateDistance && event.npc().flagValueOrDefault(Npc.LOOK_AT_PLAYER)) {
var playerPos = FabricUtil.positionFromPosAndRot(level, pos, rot);
event.npc().lookAt(playerPos).schedule(player);
}
});
}

// register listeners to handle player actions
FabricActionControllerEvents.SERVER_PLAYER_MOVE.register(this::handleMove);
FabricActionControllerEvents.SERVER_PLAYER_DISCONNECT.register(this::handleQuit);
FabricActionControllerEvents.SERVER_PLAYER_TOGGLE_SNEAK.register(this::handleToggleSneak);
FabricActionControllerEvents.SERVER_PLAYER_HAND_SWING.register(this::handlePlayerHandSwing);
FabricActionControllerEvents.SERVER_PLAYER_LEVEL_CHANGE.register(this::handleLevelChange);
}

public static @NotNull NpcActionController.Builder actionControllerBuilder(
@NotNull NpcEventManager eventManager,
@NotNull NpcTracker<ServerLevel, ServerPlayer, ItemStack, Object> npcTracker
) {
Objects.requireNonNull(eventManager, "eventManager");
Objects.requireNonNull(npcTracker, "npcTracker");

return new FabricActionControllerBuilder(eventManager, npcTracker);
}

private void handleMove(@NotNull ServerPlayer player, @Nullable Vec3 pos, @Nullable Vec2 rot) {
for (var npc : this.npcTracker.trackedNpcs()) {
// check if the player is still in the same world as the npc
var npcPos = npc.position();
var level = player.serverLevel();
if (!npc.world().equals(level) || !npc.world().hasChunk(npcPos.chunkX(), npcPos.chunkZ())) {
// if the player is tracked by the npc, stop that
npc.stopTrackingPlayer(player);
continue;
}

// check if the player moved in / out of any npc tracking distance
var playerPos = Objects.requireNonNullElse(pos, player.position());
var distance = FabricUtil.distance(npc, playerPos);
if (distance > this.spawnDistance) {
// this will only do something if the player is already tracked by the npc
npc.stopTrackingPlayer(player);
continue;
} else {
// this will only do something if the player is not already tracked by the npc
npc.trackPlayer(player);
}

// check if we should rotate the npc towards the player
if (rot != null
&& npc.tracksPlayer(player)
&& distance <= this.imitateDistance
&& npc.flagValueOrDefault(Npc.LOOK_AT_PLAYER)) {
var playerPosition = FabricUtil.positionFromPosAndRot(level, playerPos, rot);
npc.lookAt(playerPosition).schedule(player);
}
}
}

private void handleLevelChange(
@NotNull ServerPlayer player,
@Nullable ServerLevel oldLevel,
@NotNull ServerLevel newLevel
) {
// ensure that we stop tracking the player on NPCs which are not in the same world as the player
var levelId = newLevel.dimension().location().toString();
for (var npc : this.npcTracker.trackedNpcs()) {
if (!npc.position().worldId().equals(levelId)) {
// the player is no longer in the same world, stop tracking
npc.stopTrackingPlayer(player);
continue;
}

// the player is now in the same instance as the npc, check if we should track him
var distance = FabricUtil.distance(npc, player.position());
if (this.spawnDistance >= distance) {
npc.trackPlayer(player);
}
}
}

private void handleToggleSneak(@NotNull ServerPlayer player, boolean sneaking) {
var levelId = player.serverLevel().dimension().location().toString();
for (var npc : this.npcTracker.trackedNpcs()) {
// check if we should imitate the action
var distance = FabricUtil.distance(npc, player.position());
if (npc.position().worldId().equals(levelId)
&& npc.tracksPlayer(player)
&& distance <= this.imitateDistance
&& npc.flagValueOrDefault(Npc.SNEAK_WHEN_PLAYER_SNEAKS)) {
// let the npc sneak as well
npc.platform().packetFactory()
.createEntityMetaPacket(EntityMetadataFactory.sneakingMetaFactory(), sneaking)
.schedule(player, npc);
}
}
}

private void handlePlayerHandSwing(@NotNull ServerPlayer player) {
var levelId = player.serverLevel().dimension().location().toString();
for (var npc : this.npcTracker.trackedNpcs()) {
// check if we should imitate the action
var distance = FabricUtil.distance(npc, player.position());
if (npc.position().worldId().equals(levelId)
&& npc.tracksPlayer(player)
&& distance <= this.imitateDistance
&& npc.flagValueOrDefault(Npc.HIT_WHEN_PLAYER_HITS)) {
// let the npc left click as well
npc.platform().packetFactory().createAnimationPacket(EntityAnimation.SWING_MAIN_ARM).schedule(player, npc);
}
}
}

private void handleQuit(@NotNull ServerPlayer player) {
for (var npc : this.npcTracker.trackedNpcs()) {
// check if the npc tracks the player which disconnected and stop tracking him if so
npc.stopTrackingPlayer(player);
}
}

private static final class FabricActionControllerBuilder
extends CommonNpcFlaggedBuilder<Builder>
implements NpcActionController.Builder {

private final NpcEventManager eventManager;
private final NpcTracker<ServerLevel, ServerPlayer, ItemStack, Object> npcTracker;

public FabricActionControllerBuilder(
@NotNull NpcEventManager eventManager,
@NotNull NpcTracker<ServerLevel, ServerPlayer, ItemStack, Object> npcTracker
) {
this.eventManager = eventManager;
this.npcTracker = npcTracker;
}

@Override
public @NotNull NpcActionController build() {
return new FabricActionController(this.flags, this.eventManager, this.npcTracker);
}
}
}
Loading

0 comments on commit 0dcd292

Please sign in to comment.