diff --git a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java index 71f00626..680000cd 100644 --- a/src/main/java/com/minelittlepony/unicopia/InteractionManager.java +++ b/src/main/java/com/minelittlepony/unicopia/InteractionManager.java @@ -18,18 +18,21 @@ public class InteractionManager { return INSTANCE; } + /** + * Returns true on the client if the passed in player entity is the client's player. + * Always returns false on the server. + */ public boolean isClientPlayer(@Nullable PlayerEntity player) { return false; } + /** + * The player's camera mode. Always 0 on the server. + */ public int getViewMode() { return 0; } - public Race getPreferredRace() { - return Unicopia.getConfig().getPrefferedRace(); - } - /** * Side-independent method to create a new player. * diff --git a/src/main/java/com/minelittlepony/unicopia/Race.java b/src/main/java/com/minelittlepony/unicopia/Race.java index c558981a..934eb559 100644 --- a/src/main/java/com/minelittlepony/unicopia/Race.java +++ b/src/main/java/com/minelittlepony/unicopia/Race.java @@ -7,9 +7,12 @@ import java.util.function.Function; import java.util.stream.Collectors; import com.google.common.base.Strings; +import com.minelittlepony.common.client.gui.sprite.TextureSprite; +import com.minelittlepony.common.client.gui.style.Style; import com.minelittlepony.unicopia.ability.magic.Affine; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.Identifier; public enum Race implements Affine { /** @@ -101,6 +104,16 @@ public enum Race implements Affine { return this; } + public Style getStyle() { + return new Style() + .setIcon(new TextureSprite() + .setPosition(2, 2) + .setSize(16, 16) + .setTexture(new Identifier("unicopia", "textures/gui/icons.png")) + .setTextureOffset((16 * ordinal()) % 256, (ordinal() / 256) * 16) + ) + .setTooltip(getTranslationKey(), 0, 10); + } public boolean equals(String s) { return name().equalsIgnoreCase(s) diff --git a/src/main/java/com/minelittlepony/unicopia/WorldTribeManager.java b/src/main/java/com/minelittlepony/unicopia/WorldTribeManager.java new file mode 100644 index 00000000..248f8a39 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/WorldTribeManager.java @@ -0,0 +1,38 @@ +package com.minelittlepony.unicopia; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.world.PersistentState; +import net.minecraft.world.dimension.DimensionType; + +public class WorldTribeManager extends PersistentState { + + private Race defaultRace = Unicopia.getConfig().getPrefferedRace(); + + public WorldTribeManager(ServerWorld world) { + super("unicopia:tribes" + world.getDimension().getSuffix()); + } + + public Race getDefaultRace() { + return defaultRace; + } + + @Override + public void fromTag(CompoundTag tag) { + defaultRace = Race.fromName(tag.getString("defaultRace")); + } + + @Override + public CompoundTag toTag(CompoundTag tag) { + tag.putString("defaultRace", defaultRace.name()); + return tag; + } + + public static String nameFor(DimensionType dimension) { + return "unicopia:tribes" + dimension.getSuffix(); + } + + public static WorldTribeManager forWorld(ServerWorld world) { + return world.getPersistentStateManager().getOrCreate(() -> new WorldTribeManager(world), nameFor(world.getDimension())); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java b/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java index f153910a..fdd01760 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/AbilityDispatcher.java @@ -164,7 +164,7 @@ public class AbilityDispatcher implements Tickable, NbtSerialisable { T data = ability.tryActivate(player); if (data != null) { - Channel.PLAYER_ABILITY.send(new MsgPlayerAbility<>(ability, data)); + Channel.CLIENT_PLAYER_ABILITY.send(new MsgPlayerAbility<>(ability, data)); } else { setCooldown(0); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java index 9f948daf..20c9426c 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java +++ b/src/main/java/com/minelittlepony/unicopia/client/ClientInteractionManager.java @@ -4,8 +4,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.minelittlepony.unicopia.InteractionManager; -import com.minelittlepony.unicopia.Race; -import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.dummy.DummyClientPlayerEntity; import com.mojang.authlib.GameProfile; @@ -16,7 +14,6 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; public class ClientInteractionManager extends InteractionManager { - @Override @Nonnull public PlayerEntity createPlayer(Entity observer, GameProfile profile) { @@ -28,29 +25,9 @@ public class ClientInteractionManager extends InteractionManager { @Override public boolean isClientPlayer(@Nullable PlayerEntity player) { - if (MinecraftClient.getInstance().player == player) { - return true; - } - - if (MinecraftClient.getInstance().player == null || player == null) { - return false; - } - - return Pony.equal(MinecraftClient.getInstance().player, player); - } - - @Override - public Race getPreferredRace() { - if (!Unicopia.getConfig().ignoresMineLittlePony() - && MinecraftClient.getInstance().player != null) { - Race race = MineLPConnector.getPlayerPonyRace(); - - if (!race.isDefault()) { - return race; - } - } - - return Unicopia.getConfig().getPrefferedRace(); + return (MinecraftClient.getInstance().player != null && player != null) + && (MinecraftClient.getInstance().player == player + || Pony.equal(MinecraftClient.getInstance().player, player)); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/client/MineLPConnector.java b/src/main/java/com/minelittlepony/unicopia/client/MineLPConnector.java index d9e54fa2..68e8a0e2 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/MineLPConnector.java +++ b/src/main/java/com/minelittlepony/unicopia/client/MineLPConnector.java @@ -24,13 +24,14 @@ public final class MineLPConnector { case GRYPHON: case HIPPOGRIFF: case PEGASUS: - case BATPONY: return Race.PEGASUS; + case BATPONY: + return Race.BAT; case SEAPONY: case UNICORN: return Race.UNICORN; default: - return Race.EARTH; + return Race.HUMAN; } } -} \ No newline at end of file +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java b/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java index bd9284be..0c7798e8 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java +++ b/src/main/java/com/minelittlepony/unicopia/client/UnicopiaClient.java @@ -1,38 +1,70 @@ package com.minelittlepony.unicopia.client; +import com.minelittlepony.common.client.gui.element.Cycler; +import com.minelittlepony.common.event.ScreenInitCallback; +import com.minelittlepony.common.event.ScreenInitCallback.ButtonList; import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.Race; -import com.minelittlepony.unicopia.network.Channel; -import com.minelittlepony.unicopia.network.MsgRequestCapabilities; +import com.minelittlepony.unicopia.Unicopia; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.world.CreateWorldScreen; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.MathHelper; public class UnicopiaClient implements ClientModInitializer { - private Race lastPreferredRace = InteractionManager.instance().getPreferredRace(); + public static Race getPreferredRace() { + if (!Unicopia.getConfig().ignoresMineLittlePony() + && MinecraftClient.getInstance().player != null) { + Race race = MineLPConnector.getPlayerPonyRace(); + + if (!race.isDefault()) { + return race; + } + } + + return Unicopia.getConfig().getPrefferedRace(); + } @Override public void onInitializeClient() { - lastPreferredRace = InteractionManager.instance().getPreferredRace(); InteractionManager.INSTANCE = new ClientInteractionManager(); URenderers.bootstrap(); - ClientTickEvents.END_CLIENT_TICK.register(client -> { - PlayerEntity player = client.player; + ClientTickEvents.END_CLIENT_TICK.register(this::onTick); + ScreenInitCallback.EVENT.register(this::onScreenInit); + } - if (player != null && !player.removed) { - Race newRace = InteractionManager.instance().getPreferredRace(); + private void onTick(MinecraftClient client) { + KeyBindingsHandler.INSTANCE.tick(client); + } - if (newRace != lastPreferredRace) { - lastPreferredRace = newRace; - - Channel.REQUEST_CAPABILITIES.send(new MsgRequestCapabilities(lastPreferredRace)); + private void onScreenInit(Screen screen, ButtonList buttons) { + if (screen instanceof CreateWorldScreen) { + buttons.add(new Cycler(screen.width / 2 + 110, 60, 20, 20) { + @Override + protected void renderForground(MatrixStack matrices, MinecraftClient mc, int mouseX, int mouseY, int foreColor) { + super.renderForground(matrices, mc, mouseX, mouseY, foreColor); + if (isMouseOver(mouseX, mouseY)) { + renderToolTip(matrices, screen, mouseX, mouseY); + } } - } + }).setStyles( + Race.EARTH.getStyle(), + Race.UNICORN.getStyle(), + Race.PEGASUS.getStyle(), + Race.BAT.getStyle(), + Race.ALICORN.getStyle(), + Race.CHANGELING.getStyle() + ).onChange(i -> { + Unicopia.getConfig().setPreferredRace(Race.fromId(i + 1)); - KeyBindingsHandler.INSTANCE.tick(client); - }); + return i; + }).setValue(MathHelper.clamp(Unicopia.getConfig().getPrefferedRace().ordinal() - 1, 0, 5)); + } } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index fa0d4edd..48d592b7 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -5,8 +5,10 @@ import java.util.Optional; import javax.annotation.Nullable; import com.minelittlepony.unicopia.Affinity; +import com.minelittlepony.unicopia.client.UnicopiaClient; import com.minelittlepony.unicopia.InteractionManager; import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.WorldTribeManager; import com.minelittlepony.unicopia.ability.AbilityDispatcher; import com.minelittlepony.unicopia.ability.magic.AttachableSpell; import com.minelittlepony.unicopia.ability.magic.Caster; @@ -21,11 +23,13 @@ import com.minelittlepony.unicopia.item.toxin.Toxin; import com.minelittlepony.unicopia.network.Channel; import com.minelittlepony.unicopia.network.EffectSync; import com.minelittlepony.unicopia.network.MsgPlayerCapabilities; +import com.minelittlepony.unicopia.network.MsgRequestCapabilities; import com.minelittlepony.unicopia.network.Transmittable; import com.minelittlepony.unicopia.util.Copieable; import com.minelittlepony.common.util.animation.LinearInterpolator; import com.minelittlepony.common.util.animation.Interpolator; import com.mojang.authlib.GameProfile; + import net.minecraft.entity.Entity; import net.minecraft.entity.attribute.DefaultAttributeContainer; import net.minecraft.entity.damage.DamageSource; @@ -38,6 +42,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; import net.minecraft.text.Text; import net.minecraft.text.TranslatableText; import net.minecraft.util.math.BlockPos; @@ -65,7 +70,12 @@ public class Pony implements Caster, Equine, Transmi private final PlayerEntity entity; - private boolean dirty = false; + private boolean dirty; + private boolean speciesSet; + private boolean speciesPersisted; + + @Nullable + private Race clientPreferredRace; private boolean invisible = false; @@ -73,7 +83,7 @@ public class Pony implements Caster, Equine, Transmi this.entity = player; this.mana = new ManaContainer(this); - player.getDataTracker().startTracking(RACE, Race.EARTH.ordinal()); + player.getDataTracker().startTracking(RACE, Race.HUMAN.ordinal()); player.getDataTracker().startTracking(EFFECT, new CompoundTag()); player.getDataTracker().startTracking(HELD_EFFECT, new CompoundTag()); } @@ -94,6 +104,7 @@ public class Pony implements Caster, Equine, Transmi @Override public void setSpecies(Race race) { race = race.validate(entity); + speciesSet = true; entity.getDataTracker().set(RACE, race.ordinal()); @@ -110,6 +121,10 @@ public class Pony implements Caster, Equine, Transmi return invisible && hasSpell(); } + public boolean isSpeciesPersisted() { + return speciesPersisted; + } + @Override public void setInvisible(boolean invisible) { this.invisible = invisible; @@ -129,7 +144,6 @@ public class Pony implements Caster, Equine, Transmi dirty = false; if (entity instanceof ServerPlayerEntity) { - System.out.println("Sending capabilities for player"); Channel.BROADCAST_CAPABILITIES.send(new MsgPlayerCapabilities(full, this)); } } @@ -161,7 +175,25 @@ public class Pony implements Caster, Equine, Transmi @Override public boolean beforeUpdate() { - if (entity.world.isClient()) { + + if (!speciesSet && getWorld() instanceof ServerWorld) { + setSpecies(WorldTribeManager.forWorld((ServerWorld)getWorld()).getDefaultRace()); + setDirty(); + } + + if (isClientPlayer() && !speciesSet) { + Race race = UnicopiaClient.getPreferredRace(); + + if (race != clientPreferredRace) { + clientPreferredRace = race; + + if (race != getSpecies()) { + Channel.CLIENT_REQUEST_CAPABILITIES.send(new MsgRequestCapabilities(race)); + } + } + } + + if (isClient()) { if (entity.hasVehicle() && entity.isSneaking()) { Entity ridee = entity.getVehicle(); @@ -300,6 +332,7 @@ public class Pony implements Caster, Equine, Transmi @Override public void fromNBT(CompoundTag compound) { + speciesPersisted = true; setSpecies(Race.fromName(compound.getString("playerSpecies"))); powers.fromNBT(compound.getCompound("powers")); @@ -312,6 +345,7 @@ public class Pony implements Caster, Equine, Transmi @Override public void copyFrom(Pony oldPlayer) { + speciesPersisted = oldPlayer.speciesPersisted; setSpell(oldPlayer.getSpell()); setSpecies(oldPlayer.getSpecies()); setDirty(); diff --git a/src/main/java/com/minelittlepony/unicopia/network/Channel.java b/src/main/java/com/minelittlepony/unicopia/network/Channel.java index 2f5682a6..ab8b2081 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/Channel.java +++ b/src/main/java/com/minelittlepony/unicopia/network/Channel.java @@ -12,14 +12,15 @@ import net.minecraft.network.PacketByteBuf; public interface Channel { - SPacketType> PLAYER_ABILITY = clientToServer(new Identifier("unicopia", "player_ability"), MsgPlayerAbility::new); + SPacketType> CLIENT_PLAYER_ABILITY = clientToServer(new Identifier("unicopia", "player_ability"), MsgPlayerAbility::new); - SPacketType REQUEST_CAPABILITIES = clientToServer(new Identifier("unicopia", "request_capabilities"), MsgRequestCapabilities::new); - CPacketType PLAYER_CAPABILITIES = serverToClient(new Identifier("unicopia", "player_capabilities"), MsgPlayerCapabilities::new); + SPacketType CLIENT_REQUEST_CAPABILITIES = clientToServer(new Identifier("unicopia", "request_capabilities"), MsgRequestCapabilities::new); - SPacketType BROADCAST_CAPABILITIES = broadcast(PLAYER_CAPABILITIES, MsgPlayerCapabilities::new); + CPacketType SERVER_PLAYER_CAPABILITIES = serverToClient(new Identifier("unicopia", "player_capabilities"), MsgPlayerCapabilities::new); - CPacketType SPAWN_PROJECTILE = serverToClient(new Identifier("unicopia", "projectile_entity"), MsgSpawnProjectile::new); + SPacketType BROADCAST_CAPABILITIES = broadcast(SERVER_PLAYER_CAPABILITIES, MsgPlayerCapabilities::new); + + CPacketType SERVER_SPAWN_PROJECTILE = serverToClient(new Identifier("unicopia", "projectile_entity"), MsgSpawnProjectile::new); static void bootstrap() { } diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerCapabilities.java b/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerCapabilities.java index 60806593..fc7c3c4c 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerCapabilities.java +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgPlayerCapabilities.java @@ -50,7 +50,6 @@ public class MsgPlayerCapabilities implements Channel.Packet { @Override public void handle(PacketContext context) { - System.out.println("Got capabilities for player " + newRace + " " + context.getPacketEnvironment()); Pony player = Pony.of(context.getPlayer()); if (compoundTag.isEmpty()) { player.setSpecies(newRace); diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgRequestCapabilities.java b/src/main/java/com/minelittlepony/unicopia/network/MsgRequestCapabilities.java index 736c3580..bee1f5d0 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/MsgRequestCapabilities.java +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgRequestCapabilities.java @@ -1,37 +1,40 @@ package com.minelittlepony.unicopia.network; import com.minelittlepony.unicopia.Race; +import com.minelittlepony.unicopia.WorldTribeManager; import com.minelittlepony.unicopia.entity.player.Pony; import net.fabricmc.fabric.api.network.PacketContext; import net.minecraft.network.PacketByteBuf; +import net.minecraft.server.world.ServerWorld; public class MsgRequestCapabilities implements Channel.Packet { - private final Race race; + private final Race clientPreferredRace; MsgRequestCapabilities(PacketByteBuf buffer) { - race = Race.values()[buffer.readInt()]; + clientPreferredRace = Race.values()[buffer.readInt()]; } public MsgRequestCapabilities(Race preferredRace) { - race = preferredRace; + clientPreferredRace = preferredRace; } @Override public void toBuffer(PacketByteBuf buffer) { - buffer.writeInt(race.ordinal()); + buffer.writeInt(clientPreferredRace.ordinal()); } @Override public void handle(PacketContext context) { - System.out.println("Requesting player capabilities " + context.getPacketEnvironment()); Pony player = Pony.of(context.getPlayer()); - if (player.getSpecies().isDefault()) { - player.setSpecies(race); + Race worldDefaultRace = WorldTribeManager.forWorld((ServerWorld)player.getWorld()).getDefaultRace(); + + if (player.getSpecies().isDefault() || (player.getSpecies() == worldDefaultRace && !player.isSpeciesPersisted())) { + player.setSpecies(clientPreferredRace.isPermitted(context.getPlayer()) ? clientPreferredRace : worldDefaultRace); } - Channel.PLAYER_CAPABILITIES.send(context.getPlayer(), new MsgPlayerCapabilities(true, player)); + Channel.SERVER_PLAYER_CAPABILITIES.send(context.getPlayer(), new MsgPlayerCapabilities(true, player)); } } diff --git a/src/main/java/com/minelittlepony/unicopia/projectile/MagicProjectileEntity.java b/src/main/java/com/minelittlepony/unicopia/projectile/MagicProjectileEntity.java index 04b80eb1..ed4a6106 100644 --- a/src/main/java/com/minelittlepony/unicopia/projectile/MagicProjectileEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/projectile/MagicProjectileEntity.java @@ -293,6 +293,6 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Magical, @Override public Packet createSpawnPacket() { - return Channel.SPAWN_PROJECTILE.toPacket(new MsgSpawnProjectile(this)); + return Channel.SERVER_SPAWN_PROJECTILE.toPacket(new MsgSpawnProjectile(this)); } } diff --git a/src/main/resources/assets/unicopia/textures/gui/icons.png b/src/main/resources/assets/unicopia/textures/gui/icons.png new file mode 100644 index 00000000..7739ff90 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/gui/icons.png differ