generated from FabricMC/fabric-example-mod
-
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
214 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
src/main/java/io/github/misode/packtest/commands/PlayerCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package io.github.misode.packtest.commands; | ||
|
||
import com.mojang.brigadier.CommandDispatcher; | ||
import com.mojang.brigadier.arguments.StringArgumentType; | ||
import com.mojang.brigadier.context.CommandContext; | ||
import io.github.misode.packtest.fake.FakePlayer; | ||
import net.minecraft.commands.CommandSourceStack; | ||
import net.minecraft.network.chat.Component; | ||
import net.minecraft.resources.ResourceKey; | ||
import net.minecraft.server.MinecraftServer; | ||
import net.minecraft.server.level.ServerPlayer; | ||
import net.minecraft.util.RandomSource; | ||
import net.minecraft.world.level.Level; | ||
|
||
import java.util.Optional; | ||
|
||
import static net.minecraft.commands.Commands.argument; | ||
import static net.minecraft.commands.Commands.literal; | ||
|
||
public class PlayerCommand { | ||
|
||
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) { | ||
dispatcher.register(literal("player") | ||
.then(literal("spawn") | ||
.executes(PlayerCommand::spawnRandomName) | ||
.then(argument("player", StringArgumentType.word()) | ||
.executes(PlayerCommand::spawnWithName))) | ||
); | ||
} | ||
|
||
private static Optional<FakePlayer> getPlayer(CommandContext<CommandSourceStack> ctx) { | ||
String playerName = StringArgumentType.getString(ctx, "player"); | ||
return getPlayer(playerName, ctx); | ||
} | ||
|
||
private static Optional<FakePlayer> getPlayer(String playerName, CommandContext<CommandSourceStack> ctx) { | ||
MinecraftServer server = ctx.getSource().getServer(); | ||
ServerPlayer player = server.getPlayerList().getPlayerByName(playerName); | ||
if (player instanceof FakePlayer fakePlayer) { | ||
return Optional.of(fakePlayer); | ||
} | ||
return Optional.empty(); | ||
} | ||
|
||
private static int spawnRandomName(CommandContext<CommandSourceStack> ctx) { | ||
int tries = 0; | ||
while (tries++ < 10) { | ||
RandomSource random = ctx.getSource().getLevel().getRandom(); | ||
String playerName = "Tester" + random.nextInt(100, 1000); | ||
if (getPlayer(playerName, ctx).isEmpty()) { | ||
return spawn(playerName, ctx); | ||
} | ||
} | ||
ctx.getSource().sendFailure(Component.literal("Failed to spawn player with a random name")); | ||
return 0; | ||
} | ||
|
||
private static int spawnWithName(CommandContext<CommandSourceStack> ctx) { | ||
String playerName = StringArgumentType.getString(ctx, "player"); | ||
return spawn(playerName, ctx); | ||
} | ||
|
||
private static int spawn(String playerName, CommandContext<CommandSourceStack> ctx) { | ||
CommandSourceStack source = ctx.getSource(); | ||
MinecraftServer server = source.getServer(); | ||
if (getPlayer(playerName, ctx).isPresent()) { | ||
source.sendFailure(Component.literal("Player " + playerName + " is already logged on")); | ||
return 0; | ||
} | ||
ResourceKey<Level> dimension = source.getLevel().dimension(); | ||
FakePlayer.create(playerName, server, dimension, source.getPosition()); | ||
return 1; | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
src/main/java/io/github/misode/packtest/fake/FakeClientConnection.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package io.github.misode.packtest.fake; | ||
|
||
import net.minecraft.network.Connection; | ||
import net.minecraft.network.PacketListener; | ||
import net.minecraft.network.protocol.PacketFlow; | ||
|
||
public class FakeClientConnection extends Connection { | ||
|
||
public FakeClientConnection(PacketFlow flow) { | ||
super(flow); | ||
} | ||
|
||
@Override | ||
public void setReadOnly() {} | ||
|
||
@Override | ||
public void handleDisconnection() {} | ||
|
||
@Override | ||
public void setListener(PacketListener packetListener) {} | ||
} |
62 changes: 62 additions & 0 deletions
62
src/main/java/io/github/misode/packtest/fake/FakePlayer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package io.github.misode.packtest.fake; | ||
|
||
import com.mojang.authlib.GameProfile; | ||
import net.minecraft.core.UUIDUtil; | ||
import net.minecraft.network.protocol.PacketFlow; | ||
import net.minecraft.network.protocol.game.ClientboundRotateHeadPacket; | ||
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket; | ||
import net.minecraft.resources.ResourceKey; | ||
import net.minecraft.server.MinecraftServer; | ||
import net.minecraft.server.level.ClientInformation; | ||
import net.minecraft.server.level.ServerLevel; | ||
import net.minecraft.server.level.ServerPlayer; | ||
import net.minecraft.server.network.CommonListenerCookie; | ||
import net.minecraft.server.players.GameProfileCache; | ||
import net.minecraft.world.level.GameType; | ||
import net.minecraft.world.level.Level; | ||
import net.minecraft.world.level.block.entity.SkullBlockEntity; | ||
import net.minecraft.world.phys.Vec3; | ||
|
||
/** | ||
* Heavily inspired by <a href="https://github.com/gnembon/fabric-carpet/blob/master/src/main/java/carpet/patches/EntityPlayerMPFake.java">Carpet</a> | ||
*/ | ||
public class FakePlayer extends ServerPlayer { | ||
public Runnable fixStartingPosition = () -> {}; | ||
|
||
public static void create(String username, MinecraftServer server, ResourceKey<Level> dimensionId, Vec3 pos) { | ||
ServerLevel level = server.getLevel(dimensionId); | ||
GameProfileCache.setUsesAuthentication(false); | ||
GameProfile gameProfile; | ||
try { | ||
var profileCache = server.getProfileCache(); | ||
gameProfile = profileCache == null ? null : profileCache.get(username).orElse(null); | ||
} | ||
finally { | ||
GameProfileCache.setUsesAuthentication(server.isDedicatedServer() && server.usesAuthentication()); | ||
} | ||
if (gameProfile == null) { | ||
gameProfile = new GameProfile(UUIDUtil.createOfflinePlayerUUID(username), username); | ||
} | ||
GameProfile finalProfile = gameProfile; | ||
SkullBlockEntity.fetchGameProfile(gameProfile.getName()).thenAccept(p -> { | ||
GameProfile profile = p.orElse(finalProfile); | ||
FakePlayer instance = new FakePlayer(server, level, profile, ClientInformation.createDefault()); | ||
instance.fixStartingPosition = () -> instance.moveTo(pos.x, pos.y, pos.z, 0, 0); | ||
server.getPlayerList().placeNewPlayer( | ||
new FakeClientConnection(PacketFlow.SERVERBOUND), | ||
instance, | ||
new CommonListenerCookie(profile, 0, instance.clientInformation())); | ||
instance.teleportTo(level, pos.x, pos.y, pos.z, 0, 0); | ||
instance.setHealth(20); | ||
instance.unsetRemoved(); | ||
instance.gameMode.changeGameModeForPlayer(GameType.SURVIVAL); | ||
server.getPlayerList().broadcastAll(new ClientboundRotateHeadPacket(instance, (byte) (instance.yHeadRot * 256 / 360)), dimensionId); | ||
server.getPlayerList().broadcastAll(new ClientboundTeleportEntityPacket(instance), dimensionId); | ||
instance.entityData.set(DATA_PLAYER_MODE_CUSTOMISATION, (byte) 0x7f); | ||
}); | ||
} | ||
|
||
private FakePlayer(MinecraftServer server, ServerLevel level, GameProfile profile, ClientInformation cli) { | ||
super(server, level, profile, cli); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
src/main/java/io/github/misode/packtest/mixin/PlayerListMixin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package io.github.misode.packtest.mixin; | ||
|
||
import io.github.misode.packtest.fake.FakePlayer; | ||
import net.minecraft.nbt.CompoundTag; | ||
import net.minecraft.server.level.ServerPlayer; | ||
import net.minecraft.server.players.PlayerList; | ||
import org.spongepowered.asm.mixin.Mixin; | ||
import org.spongepowered.asm.mixin.injection.At; | ||
import org.spongepowered.asm.mixin.injection.Inject; | ||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; | ||
|
||
/** | ||
* Fixes starting position of fake players when they load in | ||
*/ | ||
@Mixin(PlayerList.class) | ||
public class PlayerListMixin { | ||
@Inject(method = "load", at = @At(value = "RETURN", shift = At.Shift.BEFORE)) | ||
private void fixStartingPos(ServerPlayer player, CallbackInfoReturnable<CompoundTag> cir) { | ||
if (player instanceof FakePlayer) { | ||
((FakePlayer) player).fixStartingPosition.run(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
accessWidener v1 named | ||
|
||
accessible method net/minecraft/world/level/block/entity/SkullBlockEntity fetchGameProfile (Ljava/lang/String;)Ljava/util/concurrent/CompletableFuture; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,29 @@ | ||
{ | ||
"required": true, | ||
"package": "io.github.misode.packtest.mixin", | ||
"compatibilityLevel": "JAVA_17", | ||
"mixins": [ | ||
"ArgumentTypeInfosMixin", | ||
"BlockPredicateArgumentBlockMixin", | ||
"BlockPredicateArgumentMixin", | ||
"BlockPredicateArgumentTagMixin", | ||
"CommandsMixin", | ||
"EntityArgumentMixin", | ||
"EntitySelectorMixin", | ||
"GameTestHelperMixin", | ||
"GameTestInfoMixin", | ||
"GameTestRegistryMixin", | ||
"GameTestServerMixin", | ||
"LogTestReporterMixin", | ||
"MinecraftServerMixin", | ||
"ReloadableServerResourcesMixin", | ||
"TestCommandMixin" | ||
], | ||
"server": [ | ||
"server.MainMixin" | ||
], | ||
"injectors": { | ||
"defaultRequire": 1 | ||
"required": true, | ||
"package": "io.github.misode.packtest.mixin", | ||
"compatibilityLevel": "JAVA_17", | ||
"mixins": [ | ||
"ArgumentTypeInfosMixin", | ||
"BlockPredicateArgumentBlockMixin", | ||
"BlockPredicateArgumentMixin", | ||
"BlockPredicateArgumentTagMixin", | ||
"CommandsMixin", | ||
"EntityArgumentMixin", | ||
"EntitySelectorMixin", | ||
"GameTestHelperMixin", | ||
"GameTestInfoMixin", | ||
"GameTestRegistryMixin", | ||
"GameTestServerMixin", | ||
"LogTestReporterMixin", | ||
"MinecraftServerMixin", | ||
"PlayerListMixin", | ||
"ReloadableServerResourcesMixin", | ||
"TestCommandMixin" | ||
], | ||
"server": [ | ||
"server.MainMixin" | ||
], | ||
"injectors": { | ||
"defaultRequire": 1 | ||
} | ||
} |