Sync ore depths to all clients via a packet on server login.

This commit is contained in:
modmuss50 2022-01-28 20:14:35 +00:00
parent 32368f8388
commit 5b596aa5f8
10 changed files with 224 additions and 78 deletions

View file

@ -47,8 +47,6 @@ import reborncore.common.blockentity.MachineBaseBlockEntity;
import reborncore.common.blockentity.SlotConfiguration; import reborncore.common.blockentity.SlotConfiguration;
import reborncore.common.chunkloading.ChunkLoaderManager; import reborncore.common.chunkloading.ChunkLoaderManager;
import java.util.List;
@Environment(EnvType.CLIENT) @Environment(EnvType.CLIENT)
public class ClientBoundPacketHandlers { public class ClientBoundPacketHandlers {
private static final Logger LOGGER = LoggerFactory.getLogger(ClientBoundPacketHandlers.class); private static final Logger LOGGER = LoggerFactory.getLogger(ClientBoundPacketHandlers.class);
@ -137,11 +135,7 @@ public class ClientBoundPacketHandlers {
}); });
}); });
NetworkManager.registerClientBoundHandler(new Identifier("reborncore", "sync_chunks"), (client, handler, packetBuffer, responseSender) -> { NetworkManager.registerClientBoundHandler(new Identifier("reborncore", "sync_chunks"), ChunkLoaderManager.CODEC, ClientChunkManager::setLoadedChunks);
List<ChunkLoaderManager.LoadedChunk> chunks = new ExtendedPacketBuffer(packetBuffer).readCodec(ChunkLoaderManager.CODEC);
client.execute(() -> ClientChunkManager.setLoadedChunks(chunks));
});
} }
} }

View file

@ -76,9 +76,7 @@ public class ClientBoundPackets {
} }
public static IdentifiedPacket createPacketSyncLoadedChunks(List<ChunkLoaderManager.LoadedChunk> chunks) { public static IdentifiedPacket createPacketSyncLoadedChunks(List<ChunkLoaderManager.LoadedChunk> chunks) {
return NetworkManager.createClientBoundPacket(new Identifier("reborncore", "sync_chunks"), extendedPacketBuffer -> { return NetworkManager.createClientBoundPacket(new Identifier("reborncore", "sync_chunks"), ChunkLoaderManager.CODEC, chunks);
extendedPacketBuffer.writeCodec(ChunkLoaderManager.CODEC, chunks);
});
} }
} }

View file

@ -24,11 +24,16 @@
package reborncore.common.network; package reborncore.common.network;
import com.mojang.serialization.Codec;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.fabricmc.fabric.api.networking.v1.PacketSender;
import net.fabricmc.fabric.api.networking.v1.PlayerLookup; import net.fabricmc.fabric.api.networking.v1.PlayerLookup;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.network.Packet;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.network.ServerPlayerEntity;
@ -58,10 +63,24 @@ public class NetworkManager {
return new IdentifiedPacket(identifier, buf); return new IdentifiedPacket(identifier, buf);
} }
public static <T> IdentifiedPacket createClientBoundPacket(Identifier identifier, Codec<T> codec, T value) {
return createClientBoundPacket(identifier, extendedPacketBuffer -> extendedPacketBuffer.writeCodec(codec, value));
}
// TODO move to own class
@Environment(EnvType.CLIENT)
public static void registerClientBoundHandler(Identifier identifier, ClientPlayNetworking.PlayChannelHandler handler) { public static void registerClientBoundHandler(Identifier identifier, ClientPlayNetworking.PlayChannelHandler handler) {
ClientPlayNetworking.registerGlobalReceiver(identifier, handler); ClientPlayNetworking.registerGlobalReceiver(identifier, handler);
} }
// TODO move to own class
@Environment(EnvType.CLIENT)
public static <T> void registerClientBoundHandler(Identifier identifier, Codec<T> codec, Consumer<T> consumer) {
registerClientBoundHandler(identifier, (client, handler, buf, responseSender) -> {
T value = new ExtendedPacketBuffer(buf).readCodec(codec);
client.execute(() -> consumer.accept(value));
});
}
public static void sendToServer(IdentifiedPacket packet) { public static void sendToServer(IdentifiedPacket packet) {
ClientPlayNetworking.send(packet.channel(), packet.packetByteBuf()); ClientPlayNetworking.send(packet.channel(), packet.packetByteBuf());
@ -90,5 +109,9 @@ public class NetworkManager {
} }
} }
public static void sendTo(IdentifiedPacket packet, PacketSender sender) {
Packet<?> s2CPacket = ServerPlayNetworking.createS2CPacket(packet.channel(), packet.packetByteBuf());
sender.sendPacket(s2CPacket);
}
} }

View file

@ -43,12 +43,11 @@ import techreborn.client.GuiType;
import techreborn.compat.trinkets.Trinkets; import techreborn.compat.trinkets.Trinkets;
import techreborn.config.TechRebornConfig; import techreborn.config.TechRebornConfig;
import techreborn.events.ApplyArmorToDamageHandler; import techreborn.events.ApplyArmorToDamageHandler;
import techreborn.events.ModRegistry; import techreborn.events.OreDepthSyncHandler;
import techreborn.events.UseBlockHandler; import techreborn.events.UseBlockHandler;
import techreborn.init.*; import techreborn.init.*;
import techreborn.init.template.TechRebornTemplates; import techreborn.init.template.TechRebornTemplates;
import techreborn.items.DynamicCellItem; import techreborn.items.DynamicCellItem;
import techreborn.packets.ClientboundPackets;
import techreborn.packets.ServerboundPackets; import techreborn.packets.ServerboundPackets;
import techreborn.utils.PoweredCraftingHandler; import techreborn.utils.PoweredCraftingHandler;
import techreborn.world.WorldGenerator; import techreborn.world.WorldGenerator;
@ -77,8 +76,8 @@ public class TechReborn implements ModInitializer {
ModRecipes.GRINDER.hashCode(); ModRecipes.GRINDER.hashCode();
TRContent.SCRAP_BOX.asItem(); TRContent.SCRAP_BOX.asItem();
ClientboundPackets.init();
ServerboundPackets.init(); ServerboundPackets.init();
OreDepthSyncHandler.setup();
if (TechRebornConfig.machineSoundVolume > 0) { if (TechRebornConfig.machineSoundVolume > 0) {
if (TechRebornConfig.machineSoundVolume > 1) TechRebornConfig.machineSoundVolume = 1F; if (TechRebornConfig.machineSoundVolume > 1) TechRebornConfig.machineSoundVolume = 1F;

View file

@ -70,6 +70,7 @@ import techreborn.items.FrequencyTransmitterItem;
import techreborn.items.armor.BatpackItem; import techreborn.items.armor.BatpackItem;
import techreborn.items.tool.ChainsawItem; import techreborn.items.tool.ChainsawItem;
import techreborn.items.tool.industrial.NanosaberItem; import techreborn.items.tool.industrial.NanosaberItem;
import techreborn.packets.ClientboundPackets;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -146,6 +147,7 @@ public class TechRebornClient implements ClientModInitializer {
}); });
StackToolTipHandler.setup(); StackToolTipHandler.setup();
ClientboundPackets.init();
GuiBase.wrenchStack = new ItemStack(TRContent.WRENCH); GuiBase.wrenchStack = new ItemStack(TRContent.WRENCH);
GuiBase.fluidCellProvider = DynamicCellItem::getCellWithFluid; GuiBase.fluidCellProvider = DynamicCellItem::getCellWithFluid;

View file

@ -0,0 +1,64 @@
/*
* This file is part of TechReborn, licensed under the MIT License (MIT).
*
* Copyright (c) 2020 TechReborn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package techreborn.events;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.block.Block;
import net.minecraft.util.registry.Registry;
import reborncore.common.network.NetworkManager;
import techreborn.packets.ClientboundPackets;
import techreborn.world.OreDepth;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public final class OreDepthSyncHandler {
private static Map<Block, OreDepth> oreDepthMap = new HashMap<>();
private OreDepthSyncHandler() {
}
public static void setup() {
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) ->
NetworkManager.sendTo(ClientboundPackets.createPacketSyncOreDepth(OreDepth.create(server)), sender)
);
}
public static void updateDepths(List<OreDepth> list) {
synchronized (OreDepthSyncHandler.class) {
oreDepthMap = list.stream()
.collect(Collectors.toMap(oreDepth -> Registry.BLOCK.get(oreDepth.identifier()), Function.identity()));
}
}
public static Map<Block, OreDepth> getOreDepthMap() {
synchronized (OreDepthSyncHandler.class) {
return oreDepthMap;
}
}
}

View file

@ -36,31 +36,26 @@ import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.Fluids; import net.minecraft.fluid.Fluids;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.server.integrated.IntegratedServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.LiteralText; import net.minecraft.text.LiteralText;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText; import net.minecraft.text.TranslatableText;
import net.minecraft.util.Formatting; import net.minecraft.util.Formatting;
import net.minecraft.util.registry.Registry; import net.minecraft.util.registry.Registry;
import net.minecraft.world.World;
import net.minecraft.world.gen.HeightContext;
import org.jetbrains.annotations.Nullable;
import reborncore.common.BaseBlockEntityProvider; import reborncore.common.BaseBlockEntityProvider;
import techreborn.init.TRContent; import techreborn.init.TRContent;
import techreborn.items.DynamicCellItem; import techreborn.items.DynamicCellItem;
import techreborn.items.UpgradeItem; import techreborn.items.UpgradeItem;
import techreborn.utils.ToolTipAssistUtils; import techreborn.utils.ToolTipAssistUtils;
import techreborn.world.OreDistribution; import techreborn.world.OreDepth;
import techreborn.world.TargetDimension; import techreborn.world.TargetDimension;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
public class StackToolTipHandler implements ItemTooltipCallback { public class StackToolTipHandler implements ItemTooltipCallback {
public static final Map<Item, Boolean> ITEM_ID = Maps.newHashMap(); public static final Map<Item, Boolean> ITEM_ID = Maps.newHashMap();
private static final Map<Block, OreDistribution> ORE_DISTRIBUTION_MAP = Maps.newHashMap();
private static final List<Block> UNOBTAINABLE_ORES = Lists.newLinkedList(); private static final List<Block> UNOBTAINABLE_ORES = Lists.newLinkedList();
public static void setup() { public static void setup() {
@ -71,21 +66,6 @@ public class StackToolTipHandler implements ItemTooltipCallback {
TRContent.Ores normal = ore.getUnDeepslate(); TRContent.Ores normal = ore.getUnDeepslate();
if (normal.distribution != null && normal.distribution.dimension != TargetDimension.OVERWORLD) if (normal.distribution != null && normal.distribution.dimension != TargetDimension.OVERWORLD)
UNOBTAINABLE_ORES.add(ore.block); UNOBTAINABLE_ORES.add(ore.block);
continue;
}
if (ore.distribution != null) {
ORE_DISTRIBUTION_MAP.put(ore.block, ore.distribution);
if (ore.distribution.dimension != TargetDimension.OVERWORLD) {
continue; // No Deepslate in other dims
}
TRContent.Ores deepslate = ore.getDeepslate();
if (deepslate != null) {
// Deepslate shares the same distribution as the stone version.
ORE_DISTRIBUTION_MAP.put(deepslate.block, ore.distribution);
}
} }
} }
} }
@ -120,56 +100,23 @@ public class StackToolTipHandler implements ItemTooltipCallback {
ToolTipAssistUtils.addInfo("unplaceable_fluid", tooltipLines, false); ToolTipAssistUtils.addInfo("unplaceable_fluid", tooltipLines, false);
} }
Text text = null; if (UNOBTAINABLE_ORES.contains(block)) {
if (UNOBTAINABLE_ORES.contains(block)) tooltipLines.add(new TranslatableText("techreborn.tooltip.unobtainable").formatted(Formatting.AQUA));
text = new TranslatableText("techreborn.tooltip.unobtainable"); } else if (OreDepthSyncHandler.getOreDepthMap().containsKey(block)) {
OreDistribution oreDistribution = ORE_DISTRIBUTION_MAP.get(block); OreDepth oreDepth = OreDepthSyncHandler.getOreDepthMap().get(block);
if (oreDistribution != null && text == null) { Text text = getOreDepthText(oreDepth);
text = switch (oreDistribution.dimension) {
case OVERWORLD -> getOverworldOreText(oreDistribution);
case END -> new TranslatableText("techreborn.tooltip.ores.end");
case NETHER -> new TranslatableText("techreborn.tooltip.ores.nether");
};
}
if (text != null)
tooltipLines.add(text.copy().formatted(Formatting.AQUA)); tooltipLines.add(text.copy().formatted(Formatting.AQUA));
}
} }
private static boolean isTRItem(Item item) { private static boolean isTRItem(Item item) {
return Registry.ITEM.getId(item).getNamespace().equals("techreborn"); return Registry.ITEM.getId(item).getNamespace().equals("techreborn");
} }
@Nullable private static TranslatableText getOreDepthText(OreDepth depth) {
private static HeightContext getHeightContextSafely() { return new TranslatableText("techreborn.tooltip.ores.%s".formatted(depth.dimension().name().toLowerCase(Locale.ROOT)),
final IntegratedServer server = MinecraftClient.getInstance().getServer(); new LiteralText(String.valueOf(depth.minY())).formatted(Formatting.YELLOW),
new LiteralText(String.valueOf(depth.maxY())).formatted(Formatting.YELLOW)
if (server == null) {
return null;
}
final ServerWorld world = server.getWorld(World.OVERWORLD);
if (world == null) {
return null;
}
return new HeightContext(world.getChunkManager().getChunkGenerator(), world);
}
@Nullable
private static Text getOverworldOreText(OreDistribution oreDistribution) {
final HeightContext heightContext = getHeightContextSafely();
if (heightContext == null) {
return null;
}
final int minY = oreDistribution.minOffset.getY(heightContext);
final int maxY = oreDistribution.maxY;
return new TranslatableText("techreborn.tooltip.ores.overworld",
new LiteralText(String.valueOf(minY)).formatted(Formatting.YELLOW),
new LiteralText(String.valueOf(maxY)).formatted(Formatting.YELLOW)
); );
} }
} }

View file

@ -24,10 +24,28 @@
package techreborn.packets; package techreborn.packets;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.util.Identifier;
import reborncore.common.network.IdentifiedPacket;
import reborncore.common.network.NetworkManager;
import techreborn.TechReborn;
import techreborn.events.OreDepthSyncHandler;
import techreborn.world.OreDepth;
import java.util.List;
public class ClientboundPackets { public class ClientboundPackets {
public static final Identifier ORE_DEPTH = new Identifier(TechReborn.MOD_ID, "ore_depth");
// TODO move to own class
@Environment(EnvType.CLIENT)
public static void init() { public static void init() {
NetworkManager.registerClientBoundHandler(ORE_DEPTH, OreDepth.LIST_CODEC, OreDepthSyncHandler::updateDepths);
}
public static IdentifiedPacket createPacketSyncOreDepth(List<OreDepth> oreDepths) {
return NetworkManager.createClientBoundPacket(ORE_DEPTH, OreDepth.LIST_CODEC, oreDepths);
} }
} }

View file

@ -0,0 +1,93 @@
/*
* This file is part of TechReborn, licensed under the MIT License (MIT).
*
* Copyright (c) 2020 TechReborn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package techreborn.world;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.World;
import net.minecraft.world.gen.HeightContext;
import techreborn.init.TRContent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Client synced DTO for ore depth
*/
public record OreDepth(Identifier identifier, int minY, int maxY, TargetDimension dimension) {
public static final Codec<OreDepth> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
Identifier.CODEC.fieldOf("identifier").forGetter(OreDepth::identifier),
Codec.INT.fieldOf("minY").forGetter(OreDepth::minY),
Codec.INT.fieldOf("maxY").forGetter(OreDepth::maxY),
TargetDimension.CODEC.fieldOf("dimension").forGetter(OreDepth::dimension)
).apply(instance, OreDepth::new)
);
public static Codec<List<OreDepth>> LIST_CODEC = Codec.list(OreDepth.CODEC);
public static List<OreDepth> create(MinecraftServer server) {
final List<OreDepth> depths = new ArrayList<>();
for (TRContent.Ores ore : TRContent.Ores.values()) {
if (ore.isDeepslate()) continue;
if (ore.distribution != null) {
final Identifier blockId = Registry.BLOCK.getId(ore.block);
final HeightContext heightContext = getHeightContext(server, ore.distribution.dimension);
final int minY = ore.distribution.minOffset.getY(heightContext);
final int maxY = ore.distribution.maxY;
depths.add(new OreDepth(blockId, minY, maxY, ore.distribution.dimension));
TRContent.Ores deepslate = ore.getDeepslate();
if (deepslate == null) continue;
final Identifier deepSlateBlockId = Registry.BLOCK.getId(deepslate.block);
depths.add(new OreDepth(deepSlateBlockId, minY, maxY, ore.distribution.dimension));
}
}
return Collections.unmodifiableList(depths);
}
private static HeightContext getHeightContext(MinecraftServer server, TargetDimension dimension) {
RegistryKey<World> key = switch (dimension) {
case OVERWORLD -> World.OVERWORLD;
case NETHER -> World.NETHER;
case END -> World.END;
};
final ServerWorld world = server.getWorld(key);
return new HeightContext(world.getChunkManager().getChunkGenerator(), world);
}
}

View file

@ -24,6 +24,8 @@
package techreborn.world; package techreborn.world;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.fabricmc.fabric.api.biome.v1.BiomeSelectionContext; import net.fabricmc.fabric.api.biome.v1.BiomeSelectionContext;
import net.fabricmc.fabric.api.biome.v1.BiomeSelectors; import net.fabricmc.fabric.api.biome.v1.BiomeSelectors;
@ -34,6 +36,12 @@ public enum TargetDimension {
NETHER(BiomeSelectors.foundInTheNether()), NETHER(BiomeSelectors.foundInTheNether()),
END(BiomeSelectors.foundInTheEnd()); END(BiomeSelectors.foundInTheEnd());
public static final Codec<TargetDimension> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
Codec.STRING.fieldOf("name").forGetter(TargetDimension::name)
).apply(instance, TargetDimension::valueOf)
);
public final Predicate<BiomeSelectionContext> biomeSelector; public final Predicate<BiomeSelectionContext> biomeSelector;
TargetDimension(Predicate<BiomeSelectionContext> biomeSelector) { TargetDimension(Predicate<BiomeSelectionContext> biomeSelector) {