diff --git a/src/main/java/org/ruffles/simulatedworlds/Blocks/Blocks.java b/src/main/java/org/ruffles/simulatedworlds/Blocks/Blocks.java index ef1aeba..0192886 100644 --- a/src/main/java/org/ruffles/simulatedworlds/Blocks/Blocks.java +++ b/src/main/java/org/ruffles/simulatedworlds/Blocks/Blocks.java @@ -28,7 +28,13 @@ public class Blocks { //quantum processor public static final Block QUANTUM_PROCESSOR_BLOCK = registerBlock("quantum_processor",new QuantumProcessorBlock(AbstractBlock.Settings.create())); + public static final Block BRAIN_SCANNER_BLOCK = registerBlock("brain_scanner",new BrainScannerBlock(AbstractBlock.Settings.create())); + + public static final Block HYPERSPACE_NEURAL_TENSOR_PROCESSOR_BLOCK = registerBlock("hyperspace_neural_tensor_processor",new HyperSpaceNeuralTensorProcessorBlock(AbstractBlock.Settings.create())); + public static void register() { registerBlockItem("quantum_processor", QUANTUM_PROCESSOR_BLOCK); + registerBlockItem("brain_scanner", BRAIN_SCANNER_BLOCK); + registerBlockItem("hyperspace_neural_tensor_processor", HYPERSPACE_NEURAL_TENSOR_PROCESSOR_BLOCK); } } diff --git a/src/main/java/org/ruffles/simulatedworlds/Blocks/BrainScannerBlock.java b/src/main/java/org/ruffles/simulatedworlds/Blocks/BrainScannerBlock.java new file mode 100644 index 0000000..3cc4e72 --- /dev/null +++ b/src/main/java/org/ruffles/simulatedworlds/Blocks/BrainScannerBlock.java @@ -0,0 +1,35 @@ +package org.ruffles.simulatedworlds.Blocks; + +import com.mojang.serialization.MapCodec; +import net.minecraft.block.BlockRenderType; +import net.minecraft.block.BlockState; +import net.minecraft.block.BlockWithEntity; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.WorldAccess; +import org.jetbrains.annotations.Nullable; +import org.ruffles.simulatedworlds.Entities.BlockEntities; +import org.ruffles.simulatedworlds.Entities.BrainScannerEntity; +import org.ruffles.simulatedworlds.Entities.HyperSpaceNeuralTensorProcessorEntity; +import org.ruffles.simulatedworlds.Entities.QuantumProcessorBlockEntity; + +public class BrainScannerBlock extends BlockWithEntity { + protected BrainScannerBlock(Settings settings) { + super(settings); + } + + @Override + protected MapCodec getCodec() { + return null; + } + + @Override + protected BlockRenderType getRenderType(BlockState state) { + return BlockRenderType.MODEL; + } + + @Override + public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) { + return new BrainScannerEntity(pos,state); + } +} diff --git a/src/main/java/org/ruffles/simulatedworlds/Blocks/HyperSpaceNeuralTensorProcessorBlock.java b/src/main/java/org/ruffles/simulatedworlds/Blocks/HyperSpaceNeuralTensorProcessorBlock.java new file mode 100644 index 0000000..edc5fc7 --- /dev/null +++ b/src/main/java/org/ruffles/simulatedworlds/Blocks/HyperSpaceNeuralTensorProcessorBlock.java @@ -0,0 +1,46 @@ +package org.ruffles.simulatedworlds.Blocks; + +import com.mojang.serialization.MapCodec; +import net.minecraft.block.BlockRenderType; +import net.minecraft.block.BlockState; +import net.minecraft.block.BlockWithEntity; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.WorldAccess; +import org.jetbrains.annotations.Nullable; +import org.ruffles.simulatedworlds.Entities.BlockEntities; +import org.ruffles.simulatedworlds.Entities.HyperSpaceNeuralTensorProcessorEntity; +import org.ruffles.simulatedworlds.Entities.QuantumProcessorBlockEntity; +import org.ruffles.simulatedworlds.PersistentStates.PersistedBrainData; +import org.ruffles.simulatedworlds.SimulationManager; + +import java.util.Optional; + +public class HyperSpaceNeuralTensorProcessorBlock extends BlockWithEntity { + protected HyperSpaceNeuralTensorProcessorBlock(Settings settings) { + super(settings); + } + + @Override + protected MapCodec getCodec() { + return null; + } + + @Override + protected BlockRenderType getRenderType(BlockState state) { + return BlockRenderType.MODEL; + } + + @Override + public void onBroken(WorldAccess world, BlockPos pos, BlockState state) { + super.onBroken(world, pos, state); + Optional entity = world.getBlockEntity(pos, BlockEntities.HYPERSPACE_NEURAL_TENSOR_PROCESSOR_ENTITY); + entity.ifPresent(hyperSpaceNeuralTensorProcessorEntity -> SimulationManager.detachPlayer(hyperSpaceNeuralTensorProcessorEntity.player_hash)); + } + + + @Override + public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) { + return new HyperSpaceNeuralTensorProcessorEntity(pos,state); + } +} diff --git a/src/main/java/org/ruffles/simulatedworlds/DataClasses/PlayerData.java b/src/main/java/org/ruffles/simulatedworlds/DataClasses/PlayerData.java index 737998e..1806dfe 100644 --- a/src/main/java/org/ruffles/simulatedworlds/DataClasses/PlayerData.java +++ b/src/main/java/org/ruffles/simulatedworlds/DataClasses/PlayerData.java @@ -3,30 +3,60 @@ package org.ruffles.simulatedworlds.DataClasses; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtHelper; +import net.minecraft.server.network.ServerPlayerEntity; +import org.ruffles.simulatedworlds.PersistentStates.PersistedBrainData; +import org.ruffles.simulatedworlds.Simulatedworlds; import java.util.Optional; import java.util.UUID; import static java.util.Optional.empty; +import static org.ruffles.simulatedworlds.Simulatedworlds.LOGGER; public class PlayerData { private UUID playerUUID; + private long seed; private Optional uploadID = empty(); + private Optional simid = empty(); public PlayerData() {} - public PlayerData(PlayerEntity player) { + public PlayerData(PlayerEntity player, long seed) { playerUUID = player.getGameProfile().getId(); + this.seed = seed; } public NbtCompound writeNbt(NbtCompound nbt) { nbt.putUuid("user_uuid", playerUUID); + nbt.putLong("user_seed",seed); uploadID.ifPresent(uuid -> nbt.putUuid("upload_uuid", uuid)); + simid.ifPresent(uuid -> nbt.putUuid("sim_uuid", uuid)); return nbt; } public void readNbt(NbtCompound nbt) { playerUUID = nbt.getUuid("user_uuid"); - uploadID = Optional.ofNullable(nbt.getUuid("upload_uuid")); + LOGGER.info("registered player with UUID: {}", playerUUID); + seed = nbt.getLong("user_seed"); + if (nbt.containsUuid("upload_uuid")) { + uploadID = Optional.of(nbt.getUuid("upload_uuid")); + } else { + uploadID = Optional.empty(); + } + + if (nbt.containsUuid("sim_uuid")) { + uploadID = Optional.of(nbt.getUuid("sim_uuid")); + } else { + uploadID = Optional.empty(); + } + } + + public UUID getSimID() { + return simid.orElse(null); + } + + public void setSimID(UUID simid) { + this.simid=Optional.ofNullable(simid); + PersistedBrainData.get().markDirty(); } public static PlayerData fromNbt(NbtCompound nbt) { @@ -37,6 +67,15 @@ public class PlayerData { public void setUploadID(UUID uploadID) { this.uploadID = Optional.of(uploadID); + PersistedBrainData.get().markDirty(); + } + + public ServerPlayerEntity getPlayer() { + return Simulatedworlds.SERVER.getPlayerManager().getPlayer(playerUUID); + } + + public UUID getPlayerUUID() { + return playerUUID; } public Optional getUploadID() { @@ -45,5 +84,10 @@ public class PlayerData { public void clearUploadID() { this.uploadID = empty(); + PersistedBrainData.get().markDirty(); + } + + public long getSeed() { + return seed; } } diff --git a/src/main/java/org/ruffles/simulatedworlds/DataClasses/SimulationData.java b/src/main/java/org/ruffles/simulatedworlds/DataClasses/SimulationData.java new file mode 100644 index 0000000..d34e1e6 --- /dev/null +++ b/src/main/java/org/ruffles/simulatedworlds/DataClasses/SimulationData.java @@ -0,0 +1,61 @@ +package org.ruffles.simulatedworlds.DataClasses; + +import dan200.computercraft.shared.util.NBTUtil; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.visitor.NbtElementVisitor; +import org.ruffles.simulatedworlds.PersistentStates.PersistedBrainData; +import org.ruffles.simulatedworlds.PersistentStates.PersistedSimulationData; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import static org.ruffles.simulatedworlds.Simulatedworlds.LOGGER; + +public class SimulationData { + private static HashMap playerData; + private static UUID simid; + public SimulationData() { + playerData = new HashMap<>(); + simid = UUID.randomUUID(); + } + + public NbtCompound writeNbt(NbtCompound nbt) { + nbt.putUuid("simid", simid); + NbtCompound playerDataCompound = new NbtCompound(); + for (Map.Entry entry : playerData.entrySet()) { + playerDataCompound.put(entry.getKey().toString(),entry.getValue()); + } + nbt.put("player_data",playerDataCompound); + return nbt; + } + + public void readNbt(NbtCompound nbt) { + if (nbt.containsUuid("simid")) { + simid = nbt.getUuid("simid"); + } + if (nbt.contains("player_data", NbtElement.COMPOUND_TYPE)) { + NbtCompound playerDataCompound = nbt.getCompound("player_data"); + for (String key : playerDataCompound.getKeys()) { + playerData.put(UUID.fromString(key),playerDataCompound.getCompound(key)); + } + } + } + + public void setPlayerData(UUID player_uuid, NbtCompound data) { + playerData.put(player_uuid,data); + PersistedSimulationData.get().markDirty(); + } + + public NbtCompound getPlayerData(UUID player_uuid) { + return playerData.get(player_uuid); + } + + public static SimulationData fromNbt(NbtCompound nbt) { + SimulationData simulationData = new SimulationData(); + simulationData.readNbt(nbt); + return simulationData; + } +} diff --git a/src/main/java/org/ruffles/simulatedworlds/Entities/BlockEntities.java b/src/main/java/org/ruffles/simulatedworlds/Entities/BlockEntities.java index a98823f..04f1eae 100644 --- a/src/main/java/org/ruffles/simulatedworlds/Entities/BlockEntities.java +++ b/src/main/java/org/ruffles/simulatedworlds/Entities/BlockEntities.java @@ -10,9 +10,14 @@ import org.ruffles.simulatedworlds.Blocks.Blocks; import org.ruffles.simulatedworlds.Simulatedworlds; public class BlockEntities { -public static final BlockEntityType QUANTUM_PROCESSOR_BLOCK_ENTITY = + public static final BlockEntityType QUANTUM_PROCESSOR_BLOCK_ENTITY = register("quantum_processor", QuantumProcessorBlockEntity::new, Blocks.QUANTUM_PROCESSOR_BLOCK); + public static final BlockEntityType HYPERSPACE_NEURAL_TENSOR_PROCESSOR_ENTITY = + register("hyperspace_neural_tensor_processor", HyperSpaceNeuralTensorProcessorEntity::new, Blocks.HYPERSPACE_NEURAL_TENSOR_PROCESSOR_BLOCK); + public static final BlockEntityType BRAIN_SCANNER_ENTITY = + register("brain_scanner", BrainScannerEntity::new, Blocks.BRAIN_SCANNER_BLOCK); + private static BlockEntityType register(String name, BlockEntityType.BlockEntityFactory entityFactory, Block... blocks) { diff --git a/src/main/java/org/ruffles/simulatedworlds/Entities/BrainScannerEntity.java b/src/main/java/org/ruffles/simulatedworlds/Entities/BrainScannerEntity.java new file mode 100644 index 0000000..6ab2ce8 --- /dev/null +++ b/src/main/java/org/ruffles/simulatedworlds/Entities/BrainScannerEntity.java @@ -0,0 +1,11 @@ +package org.ruffles.simulatedworlds.Entities; + +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.util.math.BlockPos; + +public class BrainScannerEntity extends BlockEntity { + public BrainScannerEntity(BlockPos pos, BlockState state) { + super(BlockEntities.BRAIN_SCANNER_ENTITY, pos, state); + } +} diff --git a/src/main/java/org/ruffles/simulatedworlds/Entities/HyperSpaceNeuralTensorProcessorEntity.java b/src/main/java/org/ruffles/simulatedworlds/Entities/HyperSpaceNeuralTensorProcessorEntity.java new file mode 100644 index 0000000..86549bb --- /dev/null +++ b/src/main/java/org/ruffles/simulatedworlds/Entities/HyperSpaceNeuralTensorProcessorEntity.java @@ -0,0 +1,41 @@ +package org.ruffles.simulatedworlds.Entities; + +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.registry.RegistryWrapper; +import net.minecraft.util.math.BlockPos; +import org.ruffles.simulatedworlds.Simulatedworlds; + +import java.util.UUID; + +public class HyperSpaceNeuralTensorProcessorEntity extends BlockEntity { + public HyperSpaceNeuralTensorProcessorEntity(BlockPos pos, BlockState state) { + super(BlockEntities.HYPERSPACE_NEURAL_TENSOR_PROCESSOR_ENTITY, pos, state); + block_uuid = UUID.randomUUID(); + } + public String player_hash; + public UUID block_uuid; + + @Override + protected void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { + if (nbt.contains("player_hash", NbtElement.STRING_TYPE)) { + player_hash = nbt.getString("player_hash"); + Simulatedworlds.LOGGER.info("HyperSpaceNeuralTensorProcessor registered: {}",player_hash); + } + if (nbt.contains("block_uuid", NbtElement.INT_ARRAY_TYPE)) { + block_uuid = nbt.getUuid("block_uuid"); + } + super.readNbt(nbt, registryLookup); + } + + @Override + protected void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { + super.writeNbt(nbt, registryLookup); + if (player_hash != null) { + nbt.putUuid("block_uuid",block_uuid); + nbt.putString("player_hash",player_hash); + } + } +} diff --git a/src/main/java/org/ruffles/simulatedworlds/Entities/QuantumProcessorBlockEntity.java b/src/main/java/org/ruffles/simulatedworlds/Entities/QuantumProcessorBlockEntity.java index 3adfb7e..cc2854d 100644 --- a/src/main/java/org/ruffles/simulatedworlds/Entities/QuantumProcessorBlockEntity.java +++ b/src/main/java/org/ruffles/simulatedworlds/Entities/QuantumProcessorBlockEntity.java @@ -3,10 +3,39 @@ package org.ruffles.simulatedworlds.Entities; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.RegistryWrapper; import net.minecraft.util.math.BlockPos; +import org.ruffles.simulatedworlds.PersistentStates.PersistedSimulationData; +import org.ruffles.simulatedworlds.SimulationManager; + +import java.util.UUID; public class QuantumProcessorBlockEntity extends BlockEntity { public QuantumProcessorBlockEntity( BlockPos pos, BlockState state) { super(BlockEntities.QUANTUM_PROCESSOR_BLOCK_ENTITY, pos, state); + simid = UUID.randomUUID(); + } + + public UUID simid; + + @Override + protected void writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { + nbt.putUuid("sim_uuid",simid); + super.writeNbt(nbt, registryLookup); + } + + @Override + protected void readNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { + if (nbt.containsUuid("sim_uuid")) { + simid = nbt.getUuid("sim_uuid"); + } + super.readNbt(nbt, registryLookup); + } + + public void registerSim() { + if (!PersistedSimulationData.get().simulationExists(simid)) { + PersistedSimulationData.get().registerSimulation(simid); + } } } diff --git a/src/main/java/org/ruffles/simulatedworlds/Mixins/PlayerMixin.java b/src/main/java/org/ruffles/simulatedworlds/Mixins/PlayerMixin.java new file mode 100644 index 0000000..a201474 --- /dev/null +++ b/src/main/java/org/ruffles/simulatedworlds/Mixins/PlayerMixin.java @@ -0,0 +1,30 @@ +package org.ruffles.simulatedworlds.Mixins; + +import net.minecraft.entity.Entity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.server.network.ServerPlayerEntity; +import org.ruffles.simulatedworlds.DataClasses.PlayerData; +import org.ruffles.simulatedworlds.DataClasses.SimulationData; +import org.ruffles.simulatedworlds.PersistentStates.PersistedBrainData; +import org.ruffles.simulatedworlds.PersistentStates.PersistedSimulationData; +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.CallbackInfoReturnable; + +@Mixin(Entity.class) +public class PlayerMixin { + @Inject(at = @At("RETURN"),method = "writeNbt") + public void writeNbt(NbtCompound nbt, CallbackInfoReturnable cir) { + Entity entity = (Entity)(Object) this; + if (entity instanceof ServerPlayerEntity player) { + PlayerData playerData = PersistedBrainData.get().getPlayerFromUUID(player.getUuid()); + if (playerData != null && playerData.getSimID() != null) { + SimulationData simdata = PersistedSimulationData.get().getSimData(playerData.getSimID()); + simdata.setPlayerData(player.getUuid(), cir.getReturnValue()); + PersistedSimulationData.get().markDirty(); + } + } + } +} diff --git a/src/main/java/org/ruffles/simulatedworlds/Peripherals/BrainScannerPeripheral.java b/src/main/java/org/ruffles/simulatedworlds/Peripherals/BrainScannerPeripheral.java new file mode 100644 index 0000000..389b12a --- /dev/null +++ b/src/main/java/org/ruffles/simulatedworlds/Peripherals/BrainScannerPeripheral.java @@ -0,0 +1,72 @@ +package org.ruffles.simulatedworlds.Peripherals; + +import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.lua.LuaValues; +import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.core.util.LuaUtil; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.math.Box; +import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; +import org.ruffles.simulatedworlds.Entities.BrainScannerEntity; +import org.ruffles.simulatedworlds.SimulationManager; + +import java.util.List; +import java.util.Optional; +import java.util.function.Supplier; + +public class BrainScannerPeripheral implements IPeripheral { + private final BrainScannerEntity brainScanner; + private Supplier> playerBrain; + private int scanned = 0; + + public BrainScannerPeripheral(BrainScannerEntity brainScanner) { + this.brainScanner = brainScanner; + } + + @Override + public @NotNull String getType() { + return "brain_scanner"; + } + + @LuaFunction + public final MethodResult start_scan() { + if (brainScanner.getWorld() == null) { return MethodResult.of(false); } + List players = brainScanner.getWorld().getEntitiesByClass(PlayerEntity.class, Box.enclosing(brainScanner.getPos().down(),brainScanner.getPos().down().down()),(e) -> true); + if (players.size() == 1) { + scanned = 0; + playerBrain = SimulationManager.scanPlayer(players.getFirst()); + return MethodResult.of(true); + } + return MethodResult.of(false); + } + + @LuaFunction + public final MethodResult stop_scan() { + if (scanned > 500) { + return MethodResult.of(false, "Unable to Stop, Stopping would cause permanent brain damage."); + } else { + return MethodResult.of(true); + } + } + + @LuaFunction + public final MethodResult scan_byte() { + if (playerBrain != null) { + Optional data = playerBrain.get(); + if (data.isPresent()) { + scanned += 1; + return MethodResult.of(((double)data.get())+128); + } else { + playerBrain = null; + } + } + return MethodResult.of(); + } + + @Override + public boolean equals(@Nullable IPeripheral other) { + return other instanceof BrainScannerPeripheral o && brainScanner == o.brainScanner; + } +} diff --git a/src/main/java/org/ruffles/simulatedworlds/Peripherals/HyperSpaceNeuralTensorProcessorPeripheral.java b/src/main/java/org/ruffles/simulatedworlds/Peripherals/HyperSpaceNeuralTensorProcessorPeripheral.java new file mode 100644 index 0000000..bc27b36 --- /dev/null +++ b/src/main/java/org/ruffles/simulatedworlds/Peripherals/HyperSpaceNeuralTensorProcessorPeripheral.java @@ -0,0 +1,115 @@ +package org.ruffles.simulatedworlds.Peripherals; + +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork; +import net.minecraft.server.network.ServerPlayerEntity; +import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; +import org.ruffles.simulatedworlds.DataClasses.PlayerData; +import org.ruffles.simulatedworlds.Entities.HyperSpaceNeuralTensorProcessorEntity; +import org.ruffles.simulatedworlds.PersistentStates.PersistedBrainData; +import org.ruffles.simulatedworlds.SimulationManager; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.UUID; + +import static org.ruffles.simulatedworlds.Simulatedworlds.LOGGER; + +public class HyperSpaceNeuralTensorProcessorPeripheral implements IPeripheral { + private final HyperSpaceNeuralTensorProcessorEntity hyperSpaceNeuralTensorProcessor; + private MessageDigest hasher; + + public HyperSpaceNeuralTensorProcessorPeripheral(HyperSpaceNeuralTensorProcessorEntity hyperSpaceNeuralTensorProcessor) { + this.hyperSpaceNeuralTensorProcessor = hyperSpaceNeuralTensorProcessor; + } + + @Override + public @NotNull String getType() { + return "hyperspace_neural_tensor_processor"; + } + + @LuaFunction + public final MethodResult start_upload() { + if (hasher != null) {return MethodResult.of(false, "Already uploading.");} + if (hyperSpaceNeuralTensorProcessor.player_hash != null) {return MethodResult.of(false, "Player already exists in processor.");} + hasher = SimulationManager.hash(); + return MethodResult.of(hasher != null); + } + + @LuaFunction + public final boolean has_player() { + return hyperSpaceNeuralTensorProcessor.player_hash != null; + } + + @LuaFunction + public final boolean clear_player() { + if (hyperSpaceNeuralTensorProcessor.player_hash != null) { + hyperSpaceNeuralTensorProcessor.player_hash = null; + return true; + } + return false; + } + + @LuaFunction + public final boolean is_uploading() { + return hasher != null; + } + + @LuaFunction + public final boolean upload_byte(double brain_byte) throws LuaException { + if (hasher == null) {return false;} + brain_byte = Math.round(brain_byte); + if (!(brain_byte <= 255 && brain_byte >= 0)) {throw new LuaException("Number out of bound expected 0<=number<=255 but got"+brain_byte);} + hasher.update((byte) (brain_byte-128)); + return true; + } + + @LuaFunction + public final boolean end_upload() { + LOGGER.info("uploading player..."); + if (hasher == null) {return false;} + String hash = new String(hasher.digest()); + LOGGER.info("attempted upload with hash: {}", hash); + if (!SimulationManager.isPlayerHash(hash)) { return false; } + hyperSpaceNeuralTensorProcessor.player_hash = hash; + SimulationManager.uploadPlayer(hyperSpaceNeuralTensorProcessor.block_uuid,hash); + hasher = null; + return true; + } + + @LuaFunction + public final MethodResult get_player_name() { + if (hyperSpaceNeuralTensorProcessor.player_hash != null) { + PlayerData pd = PersistedBrainData.get().getPlayer(hyperSpaceNeuralTensorProcessor.player_hash); + if (pd != null) { + ServerPlayerEntity player = pd.getPlayer(); + if (player != null && player.getDisplayName() != null) { + try { + return MethodResult.of(player.getDisplayName().getString()); + } catch (Exception e) { + return MethodResult.of(null, e.getMessage()); + } + } + } + } + return MethodResult.of(); + } + + @LuaFunction + public final MethodResult send_player_to_sim(String otp) { + if (SimulationManager.otpValid(otp)){ + SimulationManager.addPlayerToSimOtp(otp,hyperSpaceNeuralTensorProcessor.player_hash); + return MethodResult.of(true); + } + return MethodResult.of(false, "OTP is invalid"); + } + + @Override + public boolean equals(@Nullable IPeripheral other) { + return other instanceof HyperSpaceNeuralTensorProcessorPeripheral o && hyperSpaceNeuralTensorProcessor == o.hyperSpaceNeuralTensorProcessor; + } +} diff --git a/src/main/java/org/ruffles/simulatedworlds/Peripherals/Peripherals.java b/src/main/java/org/ruffles/simulatedworlds/Peripherals/Peripherals.java index 525eb2e..63edd9f 100644 --- a/src/main/java/org/ruffles/simulatedworlds/Peripherals/Peripherals.java +++ b/src/main/java/org/ruffles/simulatedworlds/Peripherals/Peripherals.java @@ -6,5 +6,7 @@ import org.ruffles.simulatedworlds.Entities.BlockEntities; public class Peripherals { public static void register() { PeripheralLookup.get().registerForBlockEntity((f, s) -> new QuantumProcessorPeripheral(f), BlockEntities.QUANTUM_PROCESSOR_BLOCK_ENTITY); + PeripheralLookup.get().registerForBlockEntity((f, s) -> new HyperSpaceNeuralTensorProcessorPeripheral(f), BlockEntities.HYPERSPACE_NEURAL_TENSOR_PROCESSOR_ENTITY); + PeripheralLookup.get().registerForBlockEntity((f, s) -> new BrainScannerPeripheral(f), BlockEntities.BRAIN_SCANNER_ENTITY); } } diff --git a/src/main/java/org/ruffles/simulatedworlds/Peripherals/QuantumProcessorPeripheral.java b/src/main/java/org/ruffles/simulatedworlds/Peripherals/QuantumProcessorPeripheral.java index d551b60..590e94f 100644 --- a/src/main/java/org/ruffles/simulatedworlds/Peripherals/QuantumProcessorPeripheral.java +++ b/src/main/java/org/ruffles/simulatedworlds/Peripherals/QuantumProcessorPeripheral.java @@ -5,6 +5,7 @@ import dan200.computercraft.api.peripheral.IPeripheral; import org.jetbrains.annotations.NotNull; import org.jspecify.annotations.Nullable; import org.ruffles.simulatedworlds.Entities.QuantumProcessorBlockEntity; +import org.ruffles.simulatedworlds.SimulationManager; public class QuantumProcessorPeripheral implements IPeripheral { private final QuantumProcessorBlockEntity quantumProcessor; @@ -19,8 +20,9 @@ public class QuantumProcessorPeripheral implements IPeripheral { } @LuaFunction - public final String ping() { - return "pong"; + public final String get_otp() { + quantumProcessor.registerSim(); + return SimulationManager.createOtp(quantumProcessor.simid); } @Override diff --git a/src/main/java/org/ruffles/simulatedworlds/PersistentStates/PersistedBrainData.java b/src/main/java/org/ruffles/simulatedworlds/PersistentStates/PersistedBrainData.java index b6b78e8..d2b8557 100644 --- a/src/main/java/org/ruffles/simulatedworlds/PersistentStates/PersistedBrainData.java +++ b/src/main/java/org/ruffles/simulatedworlds/PersistentStates/PersistedBrainData.java @@ -9,20 +9,22 @@ import org.jetbrains.annotations.Nullable; import org.ruffles.simulatedworlds.DataClasses.PlayerData; import java.util.HashMap; +import java.util.Map; +import java.util.UUID; import static org.ruffles.simulatedworlds.Simulatedworlds.SERVER; public class PersistedBrainData extends PersistentState { - private HashMap players = new HashMap<>(); + private final HashMap players = new HashMap<>(); public PersistedBrainData() {} @Override public NbtCompound writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { for (String s : players.keySet()) { NbtCompound pd = new NbtCompound(); - players.get(s).writeNbt(pd); + pd = players.get(s).writeNbt(pd); nbt.put(s, pd); } - return null; + return nbt; } public static PersistedBrainData fromNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registries) { @@ -34,12 +36,22 @@ public class PersistedBrainData extends PersistentState { } public void setPlayer(String k, PlayerData pd) { players.put(k,pd); + markDirty(); } public @Nullable PlayerData getPlayer(String k) { return players.get(k); } + public @Nullable PlayerData getPlayerFromUUID(UUID uuid) { + for (Map.Entry player : players.entrySet()) { + if (player.getValue().getPlayerUUID() == uuid) { + return player.getValue(); + } + } + return null; + } + // The "type descriptor" PersistentStateManager uses to construct/load your state public static final Type TYPE = new Type<>(PersistedBrainData::new, PersistedBrainData::fromNbt, DataFixTypes.LEVEL); diff --git a/src/main/java/org/ruffles/simulatedworlds/PersistentStates/PersistedSimulationData.java b/src/main/java/org/ruffles/simulatedworlds/PersistentStates/PersistedSimulationData.java index 425a7f0..5d66e06 100644 --- a/src/main/java/org/ruffles/simulatedworlds/PersistentStates/PersistedSimulationData.java +++ b/src/main/java/org/ruffles/simulatedworlds/PersistentStates/PersistedSimulationData.java @@ -8,20 +8,25 @@ import net.minecraft.nbt.NbtList; import net.minecraft.registry.RegistryWrapper; import net.minecraft.server.world.ServerWorld; import net.minecraft.world.PersistentState; +import org.ruffles.simulatedworlds.DataClasses.PlayerData; +import org.ruffles.simulatedworlds.DataClasses.SimulationData; +import org.ruffles.simulatedworlds.Simulatedworlds; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.UUID; import static org.ruffles.simulatedworlds.Simulatedworlds.SERVER; public class PersistedSimulationData extends PersistentState { - private final HashSet simulations = new HashSet<>(); + private final HashMap simulations = new HashMap<>(); public PersistedSimulationData() {} @Override public NbtCompound writeNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registryLookup) { - NbtList simulationIDs = new NbtList(); - for (UUID id : simulations) { - simulationIDs.add(NbtHelper.fromUuid(id)); + NbtCompound simulationIDs = new NbtCompound(); + for (Map.Entry entry : simulations.entrySet()) { + simulationIDs.put(entry.getKey().toString(),entry.getValue().writeNbt(new NbtCompound())); } nbt.put("simulations", simulationIDs); return nbt; @@ -29,24 +34,25 @@ public class PersistedSimulationData extends PersistentState { public static PersistedSimulationData fromNbt(NbtCompound nbt, RegistryWrapper.WrapperLookup registries) { PersistedSimulationData s = new PersistedSimulationData(); - NbtList simulationIDs = nbt.getList("simulations", NbtElement.INT_ARRAY_TYPE); - for (NbtElement simulationID : simulationIDs) { - s.simulations.add(NbtHelper.toUuid(simulationID)); + if (nbt.contains("simulations",NbtElement.COMPOUND_TYPE)) { + NbtCompound simulationIDs = nbt.getCompound("simulations"); + for (String key : simulationIDs.getKeys()) { + s.simulations.put(UUID.fromString(key),SimulationData.fromNbt(simulationIDs.getCompound(key))); + } } return s; } public boolean simulationExists(UUID simID) { - return simulations.contains(simID); + return simulations.containsKey(simID); } - public UUID registerSimulation() { - UUID candidateUuid = UUID.randomUUID(); - while (simulations.contains(candidateUuid)) { - candidateUuid = UUID.randomUUID(); - } - simulations.add(candidateUuid); - return candidateUuid; + public void registerSimulation(UUID simID) { + simulations.put(simID, new SimulationData()); + } + + public SimulationData getSimData(UUID simdata) { + return simulations.get(simdata); } // The "type descriptor" PersistentStateManager uses to construct/load your state diff --git a/src/main/java/org/ruffles/simulatedworlds/Simulatedworlds.java b/src/main/java/org/ruffles/simulatedworlds/Simulatedworlds.java index 4690042..69aca0e 100644 --- a/src/main/java/org/ruffles/simulatedworlds/Simulatedworlds.java +++ b/src/main/java/org/ruffles/simulatedworlds/Simulatedworlds.java @@ -1,23 +1,123 @@ package org.ruffles.simulatedworlds; import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.fabricmc.fabric.api.message.v1.ServerMessageEvents; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.Registries; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.Vec3d; import org.ruffles.simulatedworlds.Blocks.Blocks; +import org.ruffles.simulatedworlds.DataClasses.PlayerData; +import org.ruffles.simulatedworlds.DataClasses.SimulationData; import org.ruffles.simulatedworlds.Peripherals.Peripherals; +import org.ruffles.simulatedworlds.PersistentStates.PersistedBrainData; +import org.ruffles.simulatedworlds.PersistentStates.PersistedSimulationData; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import qouteall.dimlib.DimLibEntry; +import qouteall.dimlib.DimLibUtil; +import qouteall.dimlib.DimensionTemplate; + +import java.util.*; public class Simulatedworlds implements ModInitializer { public static String MOD_ID = "simulatedworlds"; public static Logger LOGGER = LoggerFactory.getLogger("SimulatedWorlds"); public static MinecraftServer SERVER; + public static ServerWorld LIMBO; @Override public void onInitialize() { Blocks.register(); LOGGER.info("finished registering blocks.."); Peripherals.register(); LOGGER.info("finished registering peripherals.."); - ServerLifecycleEvents.SERVER_STARTED.register(server -> SERVER = server); - ServerLifecycleEvents.SERVER_STOPPED.register(server -> SERVER = null); + ServerLifecycleEvents.SERVER_STARTED.register(server -> {SERVER = server; LIMBO = SERVER.getWorld(RegistryKey.of(RegistryKeys.WORLD, Identifier.of(MOD_ID,"limbo")));}); + ServerLifecycleEvents.SERVER_STOPPED.register(server -> {SERVER = null; LIMBO=null;}); + ServerPlayerEvents.AFTER_RESPAWN.register((oldPlayer, newPlayer, alive) -> { + UUID playeruuid = newPlayer.getUuid(); + Random random = new Random(); + random.setSeed(newPlayer.getUuid().getLeastSignificantBits()+newPlayer.getUuid().getMostSignificantBits()); + if (SimulationManager.isPlayerUploaded(playeruuid)) { + newPlayer.teleport(LIMBO,((double) random.nextInt(-3000,3000))*10000,10_000,((double) random.nextInt(-3000,3000))*10000,0,0); + } + }); + ServerPlayerEvents.LEAVE.register(SimulationManager::saveDataForPlayer); + ServerTickEvents.START_WORLD_TICK.register(serverWorld -> { + if (serverWorld == LIMBO) { + for (ServerPlayerEntity player : LIMBO.getPlayers()) { + if (player.getY() < 100) { + Random random = new Random(); + random.setSeed(player.getUuid().getLeastSignificantBits()+player.getUuid().getMostSignificantBits()); + player.teleport(LIMBO,((double) random.nextInt(-3000,3000))*10000,10_000,((double) random.nextInt(-3000,3000))*10000,player.getYaw(),player.getPitch()); + } + } + } + List players = new ArrayList<>(serverWorld.getPlayers()); + + for (ServerPlayerEntity player : players) { + Identifier worldId = serverWorld.getRegistryKey().getValue(); + PlayerData data = PersistedBrainData.get().getPlayerFromUUID(player.getUuid()); + + if (data != null && data.getSimID() != null && player.isAlive()) { + Identifier expectedWorldId = Identifier.of(MOD_ID, data.getSimID().toString()); + + if (!expectedWorldId.equals(worldId)) { + ServerWorld targetWorld = SimulationManager.getOrCreateSimulation(data.getSimID()); + + player.teleport(targetWorld, 0, 100, 0, Set.of(), player.getYaw(), player.getPitch()); + + if (PersistedSimulationData.get().simulationExists(data.getSimID())) { + SimulationData simData = PersistedSimulationData.get().getSimData(data.getSimID()); + NbtCompound playerNbt = + Objects.requireNonNullElseGet(simData.getPlayerData(player.getUuid()), NbtCompound::new); + + player.readNbt(playerNbt); + SimulationManager.saveDataForPlayer(player); + } + } + } + } + ServerMessageEvents.ALLOW_CHAT_MESSAGE.register((msg, player, params) -> { + Text text = msg.getContent(); + if (Objects.equals(player.getWorld().getRegistryKey().getValue().getNamespace(), MOD_ID)) { + for (PlayerEntity playerEntity : player.getWorld().getPlayers()) { + playerEntity.sendMessage(Text.of("<"+player.getName().getString()+"> "+text.getString())); + } + } else { + for (ServerWorld world : SERVER.getWorlds()) { + if (!Objects.equals(world.getRegistryKey().getValue().getNamespace(), MOD_ID)) { + for (PlayerEntity playerEntity : world.getPlayers()) { + playerEntity.sendMessage(Text.of("<"+player.getName().getString()+"> "+text.getString())); + } + } + } + } + + return false; + }); + }); + } } + + + + + + + + + + + + diff --git a/src/main/java/org/ruffles/simulatedworlds/SimulationManager.java b/src/main/java/org/ruffles/simulatedworlds/SimulationManager.java index d7860bf..c90a87e 100644 --- a/src/main/java/org/ruffles/simulatedworlds/SimulationManager.java +++ b/src/main/java/org/ruffles/simulatedworlds/SimulationManager.java @@ -1,53 +1,153 @@ package org.ruffles.simulatedworlds; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.util.math.random.Random; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.registry.Registries; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.Identifier; +import net.minecraft.world.dimension.DimensionOptions; import org.jetbrains.annotations.Nullable; import org.ruffles.simulatedworlds.DataClasses.PlayerData; +import org.ruffles.simulatedworlds.DataClasses.SimulationData; import org.ruffles.simulatedworlds.PersistentStates.PersistedBrainData; +import org.ruffles.simulatedworlds.PersistentStates.PersistedSimulationData; +import qouteall.dimlib.DimensionTemplate; +import qouteall.dimlib.api.DimensionAPI; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Optional; +import java.util.Random; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; -import static org.ruffles.simulatedworlds.Simulatedworlds.LOGGER; +import static org.ruffles.simulatedworlds.Simulatedworlds.*; public class SimulationManager { - public static int brain_data_size = 1000000; - public static @Nullable String hash(String str) { + public static HashMap otp_map = new HashMap<>(); + public static int brain_data_size = 300_000; + public static @Nullable MessageDigest hash() { try { - MessageDigest md = MessageDigest.getInstance("SHA-256"); - byte[] digest = md.digest(str.getBytes(StandardCharsets.UTF_8)); - return new String(digest, StandardCharsets.UTF_8); + return MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { LOGGER.error("hashing algorithm does not exist", e); return null; } } - public static @Nullable String scanPlayer(PlayerEntity player) { + public static @Nullable Supplier> scanPlayer(PlayerEntity player) { SecureRandom random = new SecureRandom(); - byte[] player_brain_data = new byte[brain_data_size]; - random.nextBytes(player_brain_data); - String string = new String(player_brain_data, StandardCharsets.UTF_8); - String hash = hash(string); - if (hash != null) { - PersistedBrainData.get().setPlayer(hash, new PlayerData(player)); - return string; - } - return null; + long seed = random.nextLong(); + random.setSeed(seed); + AtomicInteger count = new AtomicInteger(brain_data_size); + MessageDigest md = hash(); + if (md == null) {return null;} + return () -> { + byte[] bite = new byte[1]; + random.nextBytes(bite); + if (count.get() == 0) { + return Optional.empty(); + } else if (count.get() == 1) { + md.update(bite); + String hash = new String(md.digest()); + LOGGER.info("made a player with hash {}", hash); + PersistedBrainData.get().setPlayer(hash,new PlayerData(player,seed)); + player.kill(); + count.addAndGet(-1); + } else { + md.update(bite); + count.addAndGet(-1); + } + return Optional.of(bite[0]); + }; } - public static void uploadPlayer(UUID blockUUID, String brainData) { - PlayerData data = PersistedBrainData.get().getPlayer(hash(brainData)); + public static boolean isPlayerHash(String hash) { + return PersistedBrainData.get().getPlayer(hash) != null; + } + public static void uploadPlayer(UUID blockUUID, String brainDataHash) { + PlayerData data = PersistedBrainData.get().getPlayer(brainDataHash); if (data != null) { data.setUploadID(blockUUID); } } - public static void detachPlayer(String brainData) { - PlayerData data = PersistedBrainData.get().getPlayer(hash(brainData)); + public static boolean isPlayerUploaded(UUID playeruuid) { + return PersistedBrainData.get().getPlayerFromUUID(playeruuid) != null; + } + public static boolean playerInHSNTPU(String brainDataHash) { + PlayerData data = PersistedBrainData.get().getPlayer(brainDataHash); + if (data != null) { + return data.getUploadID().isPresent(); + } + return false; + } + public static void detachPlayer(String brainDataHash) { + PlayerData data = PersistedBrainData.get().getPlayer(brainDataHash); if (data != null) { data.clearUploadID(); } } + public static String createOtp(UUID simid) { + Random random = new Random(); + String generatedString = null; + while (generatedString == null || otp_map.containsKey(generatedString)) { + int leftLimit = 48; // numeral '0' + int rightLimit = 122; // letter 'z' + int targetStringLength = 10; + + generatedString = random.ints(leftLimit, rightLimit + 1) + .filter(i -> (i <= 57 || i >= 65) && (i <= 90 || i >= 97)) + .limit(targetStringLength) + .collect(StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append) + .toString(); + } + otp_map.put(generatedString,simid); + return generatedString; + } + + public static boolean otpValid(String otp) { + return otp_map.containsKey(otp); + } + + public static void addPlayerToSimOtp(String otp, String brainDataHash) { + UUID id = otp_map.get(otp); + if (id != null) { + addPlayerToSim(id,brainDataHash); + } + } + + public static void saveDataForPlayer(ServerPlayerEntity player) { + PlayerData playerData = PersistedBrainData.get().getPlayerFromUUID(player.getUuid()); + if (playerData != null && playerData.getSimID() != null) { + SimulationData simdata = PersistedSimulationData.get().getSimData(playerData.getSimID()); + simdata.setPlayerData(player.getUuid(),player.writeNbt(new NbtCompound())); + PersistedSimulationData.get().markDirty(); + } + } + + public static void addPlayerToSim(UUID simid, String brainDataHash) { + if (brainDataHash != null) { + PlayerData playerData = PersistedBrainData.get().getPlayer(brainDataHash); + if (playerData != null) { + playerData.setSimID(simid); + } + } + } + public static ServerWorld getOrCreateSimulation(UUID simid) { + ServerWorld sim = SERVER.getWorld(RegistryKey.of(RegistryKeys.WORLD,Identifier.of(MOD_ID,simid.toString()))); + if (sim == null) { + if (SERVER.isRunning()) { + DimensionAPI.addDimensionDynamically(SERVER, Identifier.of(MOD_ID, simid.toString()), new DimensionOptions(LIMBO.getDimensionEntry(), LIMBO.getChunkManager().getChunkGenerator())); + } else { + DimensionAPI.addDimension(SERVER, Identifier.of(MOD_ID, simid.toString()), new DimensionOptions(LIMBO.getDimensionEntry(), LIMBO.getChunkManager().getChunkGenerator())); + } + sim = SERVER.getWorld(RegistryKey.of(RegistryKeys.WORLD,Identifier.of(MOD_ID,simid.toString()))); + } + return sim; + } } diff --git a/src/main/resources/data/simulatedworlds/dimension/limbo.json b/src/main/resources/data/simulatedworlds/dimension/limbo.json index a087f5b..b736192 100644 --- a/src/main/resources/data/simulatedworlds/dimension/limbo.json +++ b/src/main/resources/data/simulatedworlds/dimension/limbo.json @@ -7,10 +7,6 @@ "lakes": false, "features": false, "layers": [ - { - "height": 1, - "block": "barrier" - } ] } } diff --git a/src/main/resources/data/simulatedworlds/dimension_type/limbo.json b/src/main/resources/data/simulatedworlds/dimension_type/limbo.json index 9cd8b7b..862df66 100644 --- a/src/main/resources/data/simulatedworlds/dimension_type/limbo.json +++ b/src/main/resources/data/simulatedworlds/dimension_type/limbo.json @@ -17,11 +17,12 @@ "piglin_safe": false, "bed_works": true, "respawn_anchor_works": false, - "has_raids": true, + "has_raids": false, - "logical_height": 16, + + "logical_height": 1024, "min_y": 0, - "height": 16, + "height": 1024, "infiniburn": "#minecraft:infiniburn_overworld", diff --git a/src/main/resources/simulatedworlds.mixins.json b/src/main/resources/simulatedworlds.mixins.json index d4a1519..7884a5f 100644 --- a/src/main/resources/simulatedworlds.mixins.json +++ b/src/main/resources/simulatedworlds.mixins.json @@ -1,9 +1,10 @@ { "required": true, "minVersion": "0.8", - "package": "org.ruffles.simulatedworlds.mixin", + "package": "org.ruffles.simulatedworlds.Mixins", "compatibilityLevel": "JAVA_21", "mixins": [ + "PlayerMixin" ], "injectors": { "defaultRequire": 1