Compare commits

...
8 changed files with 182 additions and 11 deletions
@@ -37,16 +37,16 @@ public class ClientPacketListenerMixin {
passengers.add(this.level.getEntity(passenger));
}
Arrays
.stream(pokemon.getOccupiedSeats())
.filter(x -> !passengers.contains(x))
.filter(Objects::nonNull)
.forEach(Entity::stopRiding);
.stream(pokemon.getOccupiedSeats())
.filter(x -> !passengers.contains(x))
.filter(Objects::nonNull)
.forEach(Entity::stopRiding);
var occupiedSeats = Arrays.asList(pokemon.getOccupiedSeats());
passengers.stream()
.filter(x -> !occupiedSeats.contains(x))
.filter(Objects::nonNull)
.forEach(x -> x.startRiding(pokemon, true));
.filter(x -> !occupiedSeats.contains(x))
.filter(Objects::nonNull)
.forEach(x -> x.startRiding(pokemon, true));
ci.cancel();
}
}
@@ -34,6 +34,7 @@ import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.writeNullableExpression
import com.cobblemon.mod.common.util.writeRidingStats
import net.minecraft.client.Minecraft
import net.minecraft.client.player.LocalPlayer
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@@ -24,10 +24,19 @@ class ClientboundUpdateRidingStatePacket(
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
if (state == null) error("Expected state to be populated for encoding")
//We need to allow for encoding the data buffer instead of state for Flashback compat
//Flashback needs to be able to call encode on the packet it gets, which only has data, not state yet.
if (state == null && data == null) error("Expected state or data to be populated for encoding")
buffer.writeInt(entity)
buffer.writeResourceLocation(behaviour)
state.encode(buffer)
if (state != null) {
state.encode(buffer)
}
else if (data != null) {
buffer.writeBytes(data)
//So the buffer can be read in the future
data.resetReaderIndex()
}
}
companion object {
+8
View File
@@ -38,6 +38,7 @@ repositories {
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven(url = "https://api.modrinth.com/maven")
maven(url = "https://maven.terraformersmc.com/")
maven(url = "https://api.modrinth.com/maven")
}
dependencies {
@@ -58,6 +59,13 @@ dependencies {
modImplementation(libs.bundles.fabric.integrations.implementation)
modRuntimeOnly(libs.bundles.fabric.integrations.runtimeOnly)
modImplementation("maven.modrinth:flashback:0.38.2-fabric,1.21.1")
// Lattice (config)
modImplementation("com.moulberry:lattice:1.2.18") {
attributes {
attribute(Attribute.of("earth.terrarium.cloche.modLoader", String::class.java), "fabric")
}
}
// modImplementation(libs.flywheelFabric)
// include(libs.flywheelFabric)
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.fabric.mixin.compat;
import com.cobblemon.mod.common.duck.PlayerDuck;
import com.cobblemon.mod.common.net.messages.client.orientation.ClientboundUpdateDriverInputPacket;
import com.llamalad7.mixinextras.sugar.Local;
import com.moulberry.flashback.record.Recorder;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.world.entity.player.Player;
import org.joml.Vector3f;
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.CallbackInfo;
import java.util.List;
@Mixin(Recorder.class)
public abstract class RecorderMixin {
@Inject(method = "writeLocalData()V", at = @At(value = "INVOKE", target = "Lcom/moulberry/flashback/io/AsyncReplaySaver;writeGamePackets(Lnet/minecraft/network/codec/StreamCodec;Ljava/util/List;)V"))
public void cobblemon$writeLocalData(
CallbackInfo ci,
@Local(name = "gamePackets") List<Packet<? super ClientGamePacketListener>> gamePackets
) {
// The server will not send a ClientboundUpdateDriverInputPacket to the player that initially set the driver input
// so Flashback needs to save that data itself. For stuff like players holding space on Pokemon mounts
LocalPlayer player = Minecraft.getInstance().player;
if (player == null) return;
// Gather driver Input
float vertInput = player.jumping ? 1.0f : (player.isShiftKeyDown() ? -1.0f : 0.0f);
Vector3f driverInput = new Vector3f(Math.signum(player.xxa), vertInput, Math.signum(player.zza));
gamePackets.add(new ClientboundCustomPayloadPacket(new ClientboundUpdateDriverInputPacket(driverInput, player.getId())));
}
}
@@ -0,0 +1,106 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.fabric.mixin.compat;
import com.cobblemon.mod.common.api.mark.Mark;
import com.cobblemon.mod.common.api.mark.Marks;
import com.cobblemon.mod.common.api.pokeball.PokeBalls;
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnExtraDataEntityPacket;
import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokemonPacket;
import com.cobblemon.mod.common.pokeball.PokeBall;
import com.cobblemon.mod.common.pokemon.Pokemon;
import com.cobblemon.mod.common.pokemon.Species;
import com.moulberry.flashback.playback.ReplayGamePacketHandler;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.minecraft.client.Minecraft;
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.game.ClientboundSetPassengersPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
@Mixin(ReplayGamePacketHandler.class)
public abstract class ReplayGamePacketHandlerMixin {
@Shadow
public abstract void handleAddEntity(ClientboundAddEntityPacket clientboundAddEntityPacket);
@Shadow
public abstract ServerLevel level();
@Shadow(remap = false)
private Int2ObjectMap<Entity> pendingEntities;
@Inject(method = "handleCustomPayload", at = @At("HEAD"))
public void cobblemon$handleSpawnEntityWithExtraData(ClientboundCustomPayloadPacket packet, CallbackInfo ci) {
if (packet.payload() instanceof SpawnExtraDataEntityPacket<?, ?> cobblemonPacket) {
ClientboundAddEntityPacket addEntityPacket = cobblemonPacket.getVanillaSpawnPacket();
this.handleAddEntity(addEntityPacket);
Entity entity = pendingEntities.get(addEntityPacket.getId());
if (entity instanceof PokemonEntity pokemonEntity && cobblemonPacket instanceof SpawnPokemonPacket spawnPokemonPacket) {
Pokemon pokemon = pokemonEntity.getPokemon();
pokemon.setUuid(spawnPokemonPacket.getPokemonUUID());
Species species = Optional.ofNullable(PokemonSpecies.INSTANCE.getByIdentifier(spawnPokemonPacket.getSpeciesId()))
.orElse(PokemonSpecies.INSTANCE.random());
pokemon.setSpecies(species);
pokemon.setScaleModifier(spawnPokemonPacket.getScaleModifier());
pokemon.setGender(spawnPokemonPacket.getGender());
pokemon.setShiny(spawnPokemonPacket.getShiny());
pokemon.setForm(pokemon.getSpecies().getFormByShowdownId(spawnPokemonPacket.getFormName()));
pokemon.setForcedAspects(spawnPokemonPacket.getAspects());
pokemon.setNickname(spawnPokemonPacket.getNickname());
ResourceLocation markId = spawnPokemonPacket.getMark();
if (markId != null) {
pokemon.setActiveMark(Marks.INSTANCE.getByIdentifier(spawnPokemonPacket.getMark()));
}
PokeBall caughtBall = PokeBalls.INSTANCE.getPokeBall(spawnPokemonPacket.getCaughtBall());
if (caughtBall != null) {
pokemon.setCaughtBall(caughtBall);
}
}
}
}
@Inject(method = "handleSetEntityPassengersPacket", at = @At("HEAD"), cancellable = true)
public void cobblemon$handleSetEntityPassengersPacket(ClientboundSetPassengersPacket packet, CallbackInfo ci) {
Entity entity = this.level().getEntity(packet.getVehicle());
if (entity instanceof PokemonEntity pokemon) {
var passengers = new ArrayList<Entity>();
for (int passenger : packet.getPassengers()) {
passengers.add(this.level().getEntity(passenger));
}
Arrays
.stream(pokemon.getOccupiedSeats())
.filter(x -> !passengers.contains(x))
.filter(Objects::nonNull)
.forEach(Entity::stopRiding);
var occupiedSeats = Arrays.asList(pokemon.getOccupiedSeats());
passengers.stream()
.filter(x -> !occupiedSeats.contains(x))
.filter(Objects::nonNull)
.forEach(x -> x.startRiding(pokemon, true));
ci.cancel();
}
}
}
@@ -9,7 +9,9 @@
"GameRendererMixin",
"GuiMixin",
"RecipeBookCategoriesMixin",
"pokeball.ModelLoaderMixin"
"pokeball.ModelLoaderMixin",
"compat.ReplayGamePacketHandlerMixin",
"compat.RecorderMixin"
],
"mixins": [
"EntityMixin",
+1 -1
View File
@@ -100,7 +100,7 @@ kotlin = [ "kotlin-stdlib", "kotlin-reflect" ]
fabric = [ "fabric-kotlin", "fabric-permissions" ]
common-integrations-compileOnly = [ "adorn-fabric", "jei-api" ]
fabric-integrations-implementation = [ "mod-menu" ]
fabric-integrations-runtimeOnly = [ "jei-fabric", "c2me-fabric" ]
fabric-integrations-runtimeOnly = [ "jei-fabric" ]
neoforge-integrations-runtimeOnly = [ "jei-neoforge", "c2me-neoforge" ]
mongo = [ "mongo-driver-core", "mongo-driver-sync" ]
unitTesting = [ "junit-fabric", "junit-params", "mockito-core", "mock-kotlin", "classgraph" ]