diff options
Diffstat (limited to 'src/main/java/gregtech/common/misc')
22 files changed, 4139 insertions, 0 deletions
diff --git a/src/main/java/gregtech/common/misc/GT_ClientPollutionMap.java b/src/main/java/gregtech/common/misc/GT_ClientPollutionMap.java new file mode 100644 index 0000000000..6e40e5860c --- /dev/null +++ b/src/main/java/gregtech/common/misc/GT_ClientPollutionMap.java @@ -0,0 +1,140 @@ +package gregtech.common.misc; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.EntityClientPlayerMP; +import net.minecraft.util.MathHelper; + +public class GT_ClientPollutionMap { + + private static final byte RADIUS = 24; + private static final byte DISTANCE_RELOAD_MAP = 5; // When player moved x chunks, shift the map to new center. + private static final byte SIZE = RADIUS * 2 + 1; // Area to keep stored. + + private int x0, z0; + private int dim; + + private boolean initialized = false; + + private static short[][] chunkMatrix; // short because reasons. + + public GT_ClientPollutionMap() {} + + public void reset() { + initialized = false; + } + + private void initialize(int playerChunkX, int playerChunkZ, int dimension) { + initialized = true; + chunkMatrix = new short[SIZE][SIZE]; + x0 = playerChunkX; + z0 = playerChunkZ; + dim = dimension; + } + + public void addChunkPollution(int chunkX, int chunkZ, int pollution) { + EntityClientPlayerMP player = Minecraft.getMinecraft().thePlayer; + if (player == null || player.worldObj == null) return; + + int playerXChunk = MathHelper.floor_double(player.posX) >> 4; + int playerZChunk = MathHelper.floor_double(player.posZ) >> 4; // posX/Z seems to be always loaded, + + if (!initialized) { + initialize(playerXChunk, playerZChunk, player.dimension); + } + + if (dim != player.dimension) { + initialize(playerXChunk, playerZChunk, player.dimension); + } + + if (Math.abs(x0 - playerXChunk) > DISTANCE_RELOAD_MAP || Math.abs(z0 - playerZChunk) > DISTANCE_RELOAD_MAP) + shiftCenter(playerXChunk, playerZChunk); + + int relX = chunkX - x0 + RADIUS; + if (relX >= SIZE || relX < 0) // out of bounds + return; + int relZ = chunkZ - z0 + RADIUS; + if (relZ >= SIZE || relZ < 0) // out of bounds + return; + + pollution = pollution / 225; + if (pollution > Short.MAX_VALUE) // Sanity + chunkMatrix[relX][relZ] = Short.MAX_VALUE; // Max pollution = 7,3mill + else if (pollution < 0) chunkMatrix[relX][relZ] = 0; + else chunkMatrix[relX][relZ] = (short) (pollution); + } + + // xy interpolation, between 4 chunks as corners, unknown treated as 0. + public int getPollution(double fx, double fz) { + if (!initialized) return 0; + int x = MathHelper.floor_double(fx); + int z = MathHelper.floor_double(fz); + int xDiff = ((x - 8) >> 4) - x0; + int zDiff = ((z - 8) >> 4) - z0; + + if (xDiff < -RADIUS || zDiff < -RADIUS || xDiff >= RADIUS || zDiff >= RADIUS) return 0; + + // coordinates in shifted chunk. + x = (x - 8) % 16; + z = (z - 8) % 16; + if (x < 0) x = 16 + x; + if (z < 0) z = 16 + z; + + int xi = 15 - x; + int zi = 15 - z; + + // read pollution in 4 corner chunks + int offsetX = RADIUS + xDiff; + int offsetZ = RADIUS + zDiff; + + int c00 = chunkMatrix[offsetX][offsetZ]; + int c10 = chunkMatrix[offsetX + 1][offsetZ]; + int c01 = chunkMatrix[offsetX][offsetZ + 1]; + int c11 = chunkMatrix[offsetX + 1][offsetZ + 1]; + + // Is divided by 15*15 but is handled when storing chunk data. + return c00 * xi * zi + c10 * x * zi + c01 * xi * z + c11 * x * z; + } + + // shift the matrix to fit new center + private void shiftCenter(int chunkX, int chunkZ) { + int xDiff = chunkX - x0; + int zDiff = chunkZ - z0; + boolean[] allEmpty = new boolean[SIZE]; // skip check z row if its empty. + if (xDiff > 0) for (byte x = 0; x < SIZE; x++) { + int xOff = x + xDiff; + if (xOff < SIZE) { + chunkMatrix[x] = chunkMatrix[xOff].clone(); + } else { + chunkMatrix[x] = new short[SIZE]; + allEmpty[x] = true; + } + } + else if (xDiff < 0) for (byte x = SIZE - 1; x >= 0; x--) { + int xOff = x + xDiff; + if (xOff > 0) { + chunkMatrix[x] = chunkMatrix[xOff].clone(); + } else { + chunkMatrix[x] = new short[SIZE]; + allEmpty[x] = true; + } + } + + if (zDiff > 0) for (byte x = 0; x < SIZE; x++) { + if (allEmpty[x]) continue; + for (int z = 0; z < SIZE; z++) { + int zOff = z + zDiff; + chunkMatrix[x][z] = (zOff < SIZE) ? chunkMatrix[x][zOff] : 0; + } + } + else if (zDiff < 0) for (byte x = 0; x < SIZE; x++) { + if (allEmpty[x]) continue; + for (int z = SIZE - 1; z >= 0; z--) { + int zOff = z + zDiff; + chunkMatrix[x][z] = (zOff > 0) ? chunkMatrix[x][zOff] : 0; + } + } + + x0 = chunkX; + z0 = chunkZ; + } +} diff --git a/src/main/java/gregtech/common/misc/GT_Command.java b/src/main/java/gregtech/common/misc/GT_Command.java new file mode 100644 index 0000000000..3bf73b6300 --- /dev/null +++ b/src/main/java/gregtech/common/misc/GT_Command.java @@ -0,0 +1,340 @@ +package gregtech.common.misc; + +import static gregtech.common.misc.WirelessNetworkManager.addEUToGlobalEnergyMap; +import static gregtech.common.misc.WirelessNetworkManager.getUserEU; +import static gregtech.common.misc.WirelessNetworkManager.setUserEU; + +import java.lang.reflect.Field; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import java.util.stream.Stream; + +import net.minecraft.command.CommandBase; +import net.minecraft.command.ICommandSender; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.util.EnumChatFormatting; + +import com.gtnewhorizon.structurelib.StructureLib; + +import gregtech.GT_Mod; +import gregtech.api.enums.GT_Values; +import gregtech.api.objects.GT_ChunkManager; +import gregtech.api.util.GT_Utility; +import gregtech.common.GT_Pollution; +import gregtech.common.misc.spaceprojects.SpaceProjectManager; + +public final class GT_Command extends CommandBase { + + @Override + public String getCommandName() { + return "gt"; + } + + @Override + public String getCommandUsage(ICommandSender sender) { + return "Usage: gt <subcommand>. Valid subcommands are: toggle, chunks, pollution."; + } + + private void printHelp(ICommandSender sender) { + sender.addChatMessage( + new ChatComponentText( + "Usage: gt <toggle|chunks|pollution|global_energy_add|global_energy_set|global_energy_join>")); + sender.addChatMessage(new ChatComponentText("\"toggle D1\" - toggles general.Debug (D1)")); + sender.addChatMessage(new ChatComponentText("\"toggle D2\" - toggles general.Debug2 (D2)")); + sender.addChatMessage(new ChatComponentText("\"toggle debugCleanroom\" - toggles cleanroom debug log")); + sender.addChatMessage(new ChatComponentText("\"toggle debugDriller\" - toggles oil drill debug log")); + sender.addChatMessage(new ChatComponentText("\"toggle debugBlockPump\" - Possible issues with pumps")); + sender.addChatMessage(new ChatComponentText("\"toggle debugBlockMiner\" - Possible issues with miners")); + sender.addChatMessage( + new ChatComponentText("\"toggle debugEntityCramming\" - How long it takes and how many entities it finds")); + sender.addChatMessage(new ChatComponentText("\"toggle debugWorldGen\" - toggles generic worldgen debug")); + sender.addChatMessage(new ChatComponentText("\"toggle debugOrevein\" - toggles worldgen ore vein debug")); + sender.addChatMessage(new ChatComponentText("\"toggle debugSmallOres\" - toggles worldgen small vein debug")); + sender.addChatMessage(new ChatComponentText("\"toggle debugStones\" - toggles worldgen stones debug")); + sender.addChatMessage(new ChatComponentText("\"toggle debugChunkloaders\" - toggles chunkloaders debug")); + sender.addChatMessage(new ChatComponentText("\"toggle debugMulti\" - toggles structurelib debug")); + sender.addChatMessage(new ChatComponentText("\"chunks\" - print a list of the force loaded chunks")); + sender.addChatMessage( + new ChatComponentText( + "\"pollution <amount>\" - adds the <amount> of the pollution to the current chunk, " + + "\n if <amount> isnt specified, will add" + + GT_Mod.gregtechproxy.mPollutionSmogLimit + + "gibbl.")); + sender.addChatMessage(new ChatComponentText(EnumChatFormatting.GOLD + " --- Global wireless EU controls ---")); + sender.addChatMessage(new ChatComponentText("Allows you to set the amount of EU in a users wireless network.")); + sender.addChatMessage( + new ChatComponentText( + "Usage:" + EnumChatFormatting.RED + + " global_energy_set " + + EnumChatFormatting.BLUE + + "[Name] " + + EnumChatFormatting.LIGHT_PURPLE + + "[EU]")); + sender.addChatMessage( + new ChatComponentText("Allows you to add EU to a users wireless network. Also accepts negative numbers.")); + sender.addChatMessage( + new ChatComponentText( + "Usage:" + EnumChatFormatting.RED + + " global_energy_add " + + EnumChatFormatting.BLUE + + "[Name] " + + EnumChatFormatting.LIGHT_PURPLE + + "[EU]")); + sender.addChatMessage( + new ChatComponentText( + "Allows you to join two users together into one network. Can be undone by writing the users name twice.")); + sender.addChatMessage( + new ChatComponentText( + "Usage:" + EnumChatFormatting.RED + + " global_energy_join " + + EnumChatFormatting.BLUE + + "[User joining] [User to join]")); + sender.addChatMessage(new ChatComponentText("Shows the amount of EU in a users energy network.")); + sender.addChatMessage( + new ChatComponentText( + "Usage:" + EnumChatFormatting.RED + " global_energy_display " + EnumChatFormatting.BLUE + "[Name]")); + } + + @Override + public List<String> addTabCompletionOptions(ICommandSender sender, String[] ss) { + List<String> l = new ArrayList<>(); + String test = ss.length == 0 ? "" : ss[0].trim(); + if (ss.length == 0 || ss.length == 1 && (test.isEmpty() || Stream + .of( + "toggle", + "chunks", + "pollution", + "global_energy_add", + "global_energy_set", + "global_energy_join", + "global_energy_display") + .anyMatch(s -> s.startsWith(test)))) { + Stream + .of( + "toggle", + "chunks", + "pollution", + "global_energy_add", + "global_energy_set", + "global_energy_join", + "global_energy_display") + .filter(s -> test.isEmpty() || s.startsWith(test)) + .forEach(l::add); + } else if (test.equals("toggle")) { + String test1 = ss[1].trim(); + Stream + .of( + "D1", + "D2", + "debugCleanroom", + "debugDriller", + "debugBlockPump", + "debugBlockMiner", + "debugWorldGen", + "debugEntityCramming", + "debugOrevein", + "debugSmallOres", + "debugStones", + "debugChunkloaders", + "debugMulti", + "debugWorldData") + .filter(s -> test1.isEmpty() || s.startsWith(test1)) + .forEach(l::add); + } + return l; + } + + @Override + public void processCommand(ICommandSender sender, String[] strings) { + if (strings.length < 1) { + printHelp(sender); + return; + } + switch (strings[0]) { + case "toggle" -> { + if (strings.length < 2) { + printHelp(sender); + return; + } + if ("debugMulti".equals(strings[1])) { + StructureLib.DEBUG_MODE = !StructureLib.DEBUG_MODE; + sender.addChatMessage( + new ChatComponentText(strings[1] + " = " + (StructureLib.DEBUG_MODE ? "true" : "false"))); + return; + } + try { + Field field = GT_Values.class.getDeclaredField(strings[1]); + if (field.getType() != boolean.class) { + sender.addChatMessage(new ChatComponentText("Wrong variable: " + strings[1])); + return; + } + boolean b = !field.getBoolean(null); + field.setBoolean(null, b); + sender.addChatMessage(new ChatComponentText(strings[1] + " = " + (b ? "true" : "false"))); + } catch (Exception e) { + sender.addChatMessage(new ChatComponentText("No such variable: " + strings[0])); + } + } + case "chunks" -> { + GT_ChunkManager.printTickets(); + sender.addChatMessage(new ChatComponentText("Forced chunks logged to GregTech.log")); + } + case "pollution" -> { + ChunkCoordinates coordinates = sender.getPlayerCoordinates(); + int amount = (strings.length < 2) ? GT_Mod.gregtechproxy.mPollutionSmogLimit + : Integer.parseInt(strings[1]); + GT_Pollution.addPollution( + sender.getEntityWorld() + .getChunkFromBlockCoords(coordinates.posX, coordinates.posZ), + amount); + } + case "global_energy_add" -> { + String username = strings[1]; + String formatted_username = EnumChatFormatting.BLUE + username + EnumChatFormatting.RESET; + UUID uuid = SpaceProjectManager.getPlayerUUIDFromName(username); + + String EU_String = strings[2]; + + // Usage is /gt global_energy_add username EU + + String EU_string_formatted = EnumChatFormatting.RED + + GT_Utility.formatNumbers(new BigInteger(EU_String)) + + EnumChatFormatting.RESET; + + if (addEUToGlobalEnergyMap(uuid, new BigInteger(EU_String))) sender.addChatMessage( + new ChatComponentText( + "Successfully added " + EU_string_formatted + + "EU to the global energy network of " + + formatted_username + + ".")); + else sender.addChatMessage( + new ChatComponentText( + "Failed to add " + EU_string_formatted + + "EU to the global energy map of " + + formatted_username + + ". Insufficient energy in network. ")); + + sender.addChatMessage( + new ChatComponentText( + formatted_username + " currently has " + + EnumChatFormatting.RED + + GT_Utility.formatNumbers(new BigInteger(getUserEU(uuid).toString())) + + EnumChatFormatting.RESET + + "EU in their network.")); + + } + case "global_energy_set" -> { + + // Usage is /gt global_energy_set username EU + + String username = strings[1]; + String formatted_username = EnumChatFormatting.BLUE + username + EnumChatFormatting.RESET; + UUID uuid = SpaceProjectManager.getPlayerUUIDFromName(username); + + String EU_String_0 = strings[2]; + + if ((new BigInteger(EU_String_0).compareTo(BigInteger.ZERO)) < 0) { + sender.addChatMessage( + new ChatComponentText("Cannot set a users energy network to a negative value.")); + break; + } + + setUserEU(uuid, new BigInteger(EU_String_0)); + + sender.addChatMessage( + new ChatComponentText( + "Successfully set " + formatted_username + + "'s global energy network to " + + EnumChatFormatting.RED + + GT_Utility.formatNumbers(new BigInteger(EU_String_0)) + + EnumChatFormatting.RESET + + "EU.")); + + } + case "global_energy_join" -> { + + // Usage is /gt global_energy_join username_of_you username_to_join + + String usernameSubject = strings[1]; + String usernameTeam = strings[2]; + + String formattedUsernameSubject = EnumChatFormatting.BLUE + usernameSubject + EnumChatFormatting.RESET; + String formattedUsernameTeam = EnumChatFormatting.BLUE + usernameTeam + EnumChatFormatting.RESET; + + UUID uuidSubject = SpaceProjectManager.getPlayerUUIDFromName(usernameSubject); + UUID uuidTeam = SpaceProjectManager.getLeader(SpaceProjectManager.getPlayerUUIDFromName(usernameTeam)); + + if (uuidSubject.equals(uuidTeam)) { + // leave team + SpaceProjectManager.putInTeam(uuidSubject, uuidSubject); + sender.addChatMessage( + new ChatComponentText( + "User " + formattedUsernameSubject + " has rejoined their own global energy network.")); + break; + } + + // join other's team + + if (uuidSubject.equals(uuidTeam)) { + sender.addChatMessage(new ChatComponentText("They are already in the same network!")); + break; + } + + SpaceProjectManager.putInTeam(uuidSubject, uuidTeam); + + sender.addChatMessage( + new ChatComponentText( + "Success! " + formattedUsernameSubject + " has joined " + formattedUsernameTeam + ".")); + sender.addChatMessage( + new ChatComponentText( + "To undo this simply join your own network again with /gt global_energy_join " + + formattedUsernameSubject + + " " + + formattedUsernameSubject + + ".")); + + } + case "global_energy_display" -> { + + // Usage is /gt global_energy_display username. + + String username = strings[1]; + String formatted_username = EnumChatFormatting.BLUE + username + EnumChatFormatting.RESET; + UUID userUUID = SpaceProjectManager.getPlayerUUIDFromName(username); + + if (!SpaceProjectManager.isInTeam(userUUID)) { + sender.addChatMessage( + new ChatComponentText("User " + formatted_username + " has no global energy network.")); + break; + } + UUID teamUUID = SpaceProjectManager.getLeader(userUUID); + + sender.addChatMessage( + new ChatComponentText( + "User " + formatted_username + + " has " + + EnumChatFormatting.RED + + GT_Utility.formatNumbers(getUserEU(userUUID)) + + EnumChatFormatting.RESET + + "EU in their network.")); + if (!userUUID.equals(teamUUID)) sender.addChatMessage( + new ChatComponentText( + "User " + formatted_username + + " is currently in network of " + + EnumChatFormatting.BLUE + + SpaceProjectManager.getPlayerNameFromUUID(teamUUID) + + EnumChatFormatting.RESET + + ".")); + + } + default -> { + sender + .addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "Invalid command/syntax detected.")); + printHelp(sender); + } + } + } +} diff --git a/src/main/java/gregtech/common/misc/GT_DrillingLogicDelegate.java b/src/main/java/gregtech/common/misc/GT_DrillingLogicDelegate.java new file mode 100644 index 0000000000..9cf7fd7cf8 --- /dev/null +++ b/src/main/java/gregtech/common/misc/GT_DrillingLogicDelegate.java @@ -0,0 +1,266 @@ +package gregtech.common.misc; + +import static gregtech.api.enums.GT_Values.debugBlockMiner; + +import java.util.List; + +import net.minecraft.block.Block; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraftforge.common.util.FakePlayer; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.api.util.GT_Log; +import gregtech.api.util.GT_ModHandler; +import gregtech.api.util.GT_Utility; +import gregtech.common.blocks.GT_TileEntity_Ores; + +/** @author Relvl on 27.01.2022 */ +@SuppressWarnings("ObjectEquality") +public class GT_DrillingLogicDelegate { + + public static final ItemStack MINING_PIPE_STACK = GT_ModHandler.getIC2Item("miningPipe", 0); + public static final Block MINING_PIPE_BLOCK = GT_Utility.getBlockFromStack(MINING_PIPE_STACK); + public static final Block MINING_PIPE_TIP_BLOCK = GT_Utility + .getBlockFromStack(GT_ModHandler.getIC2Item("miningPipeTip", 0)); + + /** The owner machine pointer */ + private final GT_IDrillingLogicDelegateOwner owner; + + /** Is pipe retracting process done and halts? */ + private boolean isRetractDone; + /** Is machine ran out of mining pipes in its inventory and halts? */ + private boolean isWaitingForPipeItem; + /** Pipe tip depth (relative to machine Y position, NEGATIVE). */ + private int tipDepth; + /** Cached fake player */ + private FakePlayer mFakePlayer; + + public GT_DrillingLogicDelegate(GT_IDrillingLogicDelegateOwner owner) { + this.owner = owner; + } + + /** Descents a pipe tip one plock deeper. */ + public boolean descent(IGregTechTileEntity te) { + if (!te.isAllowedToWork()) { + return false; + } + + int xCoord = te.getXCoord(); + int zCoord = te.getZCoord(); + int yCoord = te.getYCoord(); + int checkY = yCoord + tipDepth - 1; + boolean isHitsTheVoid = checkY < 0; + boolean isHitsBedrock = GT_Utility.getBlockHardnessAt(te.getWorld(), xCoord, checkY, zCoord) < 0; + boolean isFakePlayerAllowed = canFakePlayerInteract(te, xCoord, checkY, zCoord); + + if (isHitsTheVoid || isHitsBedrock || !isFakePlayerAllowed) { + // Disable and start retracting process. + te.disableWorking(); + if (debugBlockMiner) { + if (isHitsTheVoid) { + GT_Log.out.println("MINER: Hit bottom"); + } + if (isHitsBedrock) { + GT_Log.out.println("MINER: Hit block with -1 hardness"); + } + if (!isFakePlayerAllowed) { + GT_Log.out.println("MINER: Unable to set mining pipe tip"); + } + } + return false; + } + + // Replace the tip onto pipe + if (te.getBlockOffset(0, tipDepth, 0) == MINING_PIPE_TIP_BLOCK) { + te.getWorld() + .setBlock(xCoord, yCoord + tipDepth, zCoord, MINING_PIPE_BLOCK); + } + // Get and decrease pipe from the machine + boolean pipeTaken = owner.pullInputs(MINING_PIPE_STACK.getItem(), 1, false); + if (!pipeTaken) { + // If there was nothing - waiting for the pipes (just for prevent unnecessary checks) + isWaitingForPipeItem = true; + return false; + } + + // If there is something - mine it + Block block = te.getBlockOffset(0, tipDepth - 1, 0); + if (!block.isAir(te.getWorld(), xCoord, yCoord, zCoord)) { + mineBlock(te, block, xCoord, yCoord + tipDepth - 1, zCoord); + } + + // Descent the pipe tip + te.getWorld() + .setBlock(xCoord, yCoord + tipDepth - 1, zCoord, MINING_PIPE_TIP_BLOCK); + tipDepth--; + return true; + } + + public void onOwnerPostTick(IGregTechTileEntity te, long tick) { + // If the machine was disabled - try to retract pipe + if (!te.isAllowedToWork()) { + onPostTickRetract(te, tick); + return; + } + // If the machine was re-enabled - we should reset the retracting process + isRetractDone = false; + } + + /** If the machine are disabled - tried to retract pipe. */ + private void onPostTickRetract(IGregTechTileEntity aBaseMetaTileEntity, long aTick) { + if (isRetractDone) { + return; + } + // If retracting process just touch the miner + if (tipDepth == 0) { + isRetractDone = true; + return; + } + // Once per N ticks (depends on tier) + if ((aTick % (owner.getMachineSpeed() / 5)) != 0) { + return; + } + + // Check we can push pipe back to machine (inputs allowed for this case!) + boolean canPush = owner.pushOutputs(MINING_PIPE_STACK, 1, true, true); + if (!canPush) { + return; + } + + // Inspect target block - it should be a pipe tip, else something went wrong. + Block targetBlock = aBaseMetaTileEntity.getBlockOffset(0, tipDepth, 0); + if (targetBlock != MINING_PIPE_TIP_BLOCK && targetBlock != MINING_PIPE_BLOCK) { + return; + } + + // Retract the pipe/tip + int xCoord = aBaseMetaTileEntity.getXCoord(); + int yCoord = aBaseMetaTileEntity.getYCoord(); + int zCoord = aBaseMetaTileEntity.getZCoord(); + int actualDrillY = yCoord + tipDepth; + // Move the pipe tip position + if (actualDrillY < yCoord - 1) { + owner.getBaseMetaTileEntity() + .getWorld() + .setBlock(xCoord, actualDrillY + 1, zCoord, MINING_PIPE_TIP_BLOCK); + } + // Remove the old pipe tip + aBaseMetaTileEntity.getWorld() + .setBlock(xCoord, actualDrillY, zCoord, Blocks.air, 0, /* send to client without neighbour updates */ 2); + + // Return the pipe back to the machine (inputs allowed for this case!) + owner.pushOutputs(MINING_PIPE_STACK, 1, false, true); + + tipDepth++; + } + + /** Minings the block if it is possible. */ + public void mineBlock(IGregTechTileEntity te, Block block, int x, int y, int z) { + if (!GT_Utility.eraseBlockByFakePlayer(getFakePlayer(te), x, y, z, true)) { + return; + } + + List<ItemStack> drops = getBlockDrops(block, x, y, z); + + boolean canFitDrops = true; + for (ItemStack drop : drops) { + canFitDrops &= owner.pushOutputs(drop, drop.stackSize, true, false); + } + if (!canFitDrops) { + return; + } + for (ItemStack drop : drops) { + owner.pushOutputs(drop, drop.stackSize, false, false); + } + + short metaData = 0; + TileEntity tTileEntity = owner.getBaseMetaTileEntity() + .getTileEntity(x, y, z); + if (tTileEntity instanceof GT_TileEntity_Ores) { + metaData = ((GT_TileEntity_Ores) tTileEntity).mMetaData; + } + + ItemStack cobble = GT_Utility.getCobbleForOre(block, metaData); + te.getWorld() + .setBlock( + x, + y, + z, + Block.getBlockFromItem(cobble.getItem()), + cobble.getItemDamage(), /* cause updates(1) + send to client(2) */ + 3); + } + + /** + * Returns NEGATIVE (eg -5) depth of current drilling Y world level. RELATIVELY TO MINER ENTITY! This means '(miner + * world Y) + depth = (actual world Y)'. + */ + public int getTipDepth() { + return tipDepth; + } + + /** Looking for the lowest continuous pipe. */ + public void findTipDepth() { + IGregTechTileEntity ownerTe = owner.getBaseMetaTileEntity(); + if (!ownerTe.isServerSide()) { + return; + } + while (true) { + Block block = ownerTe.getBlockOffset(0, tipDepth - 1, 0); + if (block != MINING_PIPE_BLOCK && block != MINING_PIPE_TIP_BLOCK) { + return; + } + tipDepth--; + } + } + + /** + * Creates and provides the Fake Player for owners. todo maybe will provide player owner uuid? im sure some servers + * not allow to fakers, in griefing reasons. + */ + public FakePlayer getFakePlayer(IGregTechTileEntity te) { + if (mFakePlayer == null) { + mFakePlayer = GT_Utility.getFakePlayer(te); + } + if (mFakePlayer != null) { + mFakePlayer.setWorld(te.getWorld()); + mFakePlayer.setPosition(te.getXCoord(), te.getYCoord(), te.getZCoord()); + } + return mFakePlayer; + } + + public boolean canFakePlayerInteract(IGregTechTileEntity te, int xCoord, int yCoord, int zCoord) { + return GT_Utility + .setBlockByFakePlayer(getFakePlayer(te), xCoord, yCoord, zCoord, MINING_PIPE_TIP_BLOCK, 0, true); + } + + /** Get target block drops. We need to encapsulate everyting of mining in this class. */ + private List<ItemStack> getBlockDrops(final Block oreBlock, int posX, int posY, int posZ) { + return oreBlock.getDrops( + owner.getBaseMetaTileEntity() + .getWorld(), + posX, + posY, + posZ, + owner.getBaseMetaTileEntity() + .getMetaID(posX, posY, posZ), + owner.getMachineTier()); + } + + /** Can the owner continue doing its work? If we await new pipes - it cannot. */ + public boolean canContinueDrilling(long tick) { + if (isWaitingForPipeItem) { + if (tick % 5 != 0) { + return false; + } + boolean hasPipe = owner.pullInputs(MINING_PIPE_STACK.getItem(), 1, true); + if (hasPipe) { + isWaitingForPipeItem = false; + } + return hasPipe; + } + return true; + } +} diff --git a/src/main/java/gregtech/common/misc/GT_IDrillingLogicDelegateOwner.java b/src/main/java/gregtech/common/misc/GT_IDrillingLogicDelegateOwner.java new file mode 100644 index 0000000000..9b10b6fcf0 --- /dev/null +++ b/src/main/java/gregtech/common/misc/GT_IDrillingLogicDelegateOwner.java @@ -0,0 +1,22 @@ +package gregtech.common.misc; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +import gregtech.api.interfaces.metatileentity.IMetaTileEntity; + +/** @author Relvl on 27.01.2022 */ +public interface GT_IDrillingLogicDelegateOwner extends IMetaTileEntity { + + /** Returns the machine actual tier. */ + int getMachineTier(); + + /** Returns the machine current processing speed. */ + int getMachineSpeed(); + + /** Pulls (or check can pull) items from an input slots. */ + boolean pullInputs(Item item, int count, boolean simulate); + + /** Pushes (or check can push) item to output slots. */ + boolean pushOutputs(ItemStack stack, int count, boolean simulate, boolean allowInputSlots); +} diff --git a/src/main/java/gregtech/common/misc/GlobalEnergyWorldSavedData.java b/src/main/java/gregtech/common/misc/GlobalEnergyWorldSavedData.java new file mode 100644 index 0000000000..1a03012649 --- /dev/null +++ b/src/main/java/gregtech/common/misc/GlobalEnergyWorldSavedData.java @@ -0,0 +1,125 @@ +package gregtech.common.misc; + +import static gregtech.common.misc.GlobalVariableStorage.GlobalEnergy; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraft.world.WorldSavedData; +import net.minecraft.world.storage.MapStorage; +import net.minecraftforge.event.world.WorldEvent; + +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import gregtech.common.misc.spaceprojects.SpaceProjectManager; + +public class GlobalEnergyWorldSavedData extends WorldSavedData { + + public static GlobalEnergyWorldSavedData INSTANCE; + + private static final String DATA_NAME = "GregTech_WirelessEUWorldSavedData"; + + private static final String GlobalEnergyNBTTag = "GregTech_GlobalEnergy_MapNBTTag"; + private static final String GlobalEnergyTeamNBTTag = "GregTech_GlobalEnergyTeam_MapNBTTag"; + + private static void loadInstance(World world) { + + GlobalEnergy.clear(); + + MapStorage storage = world.mapStorage; + INSTANCE = (GlobalEnergyWorldSavedData) storage.loadData(GlobalEnergyWorldSavedData.class, DATA_NAME); + if (INSTANCE == null) { + INSTANCE = new GlobalEnergyWorldSavedData(); + storage.setData(DATA_NAME, INSTANCE); + } + INSTANCE.markDirty(); + } + + @SuppressWarnings("unused") + @SubscribeEvent + public void onWorldLoad(WorldEvent.Load event) { + if (!event.world.isRemote && event.world.provider.dimensionId == 0) { + loadInstance(event.world); + } + } + + public GlobalEnergyWorldSavedData() { + super(DATA_NAME); + } + + @SuppressWarnings("unused") + public GlobalEnergyWorldSavedData(String name) { + super(name); + } + + @Override + @SuppressWarnings("unchecked") + public void readFromNBT(NBTTagCompound nbtTagCompound) { + + try { + byte[] ba = nbtTagCompound.getByteArray(GlobalEnergyNBTTag); + InputStream byteArrayInputStream = new ByteArrayInputStream(ba); + ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); + Object data = objectInputStream.readObject(); + HashMap<Object, BigInteger> hashData = (HashMap<Object, BigInteger>) data; + for (Map.Entry<Object, BigInteger> entry : hashData.entrySet()) { + try { + GlobalEnergy.put( + UUID.fromString( + entry.getKey() + .toString()), + entry.getValue()); + } catch (RuntimeException ignored) { + // probably a malformed uuid. in any case, try carry on with the load + } + } + } catch (IOException | ClassNotFoundException exception) { + System.out.println(GlobalEnergyNBTTag + " FAILED"); + exception.printStackTrace(); + } + try { + if (!nbtTagCompound.hasKey(GlobalEnergyTeamNBTTag)) return; + byte[] ba = nbtTagCompound.getByteArray(GlobalEnergyTeamNBTTag); + InputStream byteArrayInputStream = new ByteArrayInputStream(ba); + ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); + Object data = objectInputStream.readObject(); + HashMap<String, String> oldTeams = (HashMap<String, String>) data; + for (String member : oldTeams.keySet()) { + String leader = oldTeams.get(member); + try { + SpaceProjectManager.putInTeam(UUID.fromString(member), UUID.fromString(leader)); + } catch (RuntimeException ignored) { + // probably a malformed uuid. in any case, try carry on with the load + } + } + } catch (IOException | ClassNotFoundException exception) { + System.out.println(GlobalEnergyTeamNBTTag + " FAILED"); + exception.printStackTrace(); + } + } + + @Override + public void writeToNBT(NBTTagCompound nbtTagCompound) { + + try { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); + objectOutputStream.writeObject(GlobalEnergy); + objectOutputStream.flush(); + byte[] data = byteArrayOutputStream.toByteArray(); + nbtTagCompound.setByteArray(GlobalEnergyNBTTag, data); + } catch (IOException exception) { + System.out.println(GlobalEnergyNBTTag + " SAVE FAILED"); + exception.printStackTrace(); + } + } +} diff --git a/src/main/java/gregtech/common/misc/GlobalMetricsCoverDatabase.java b/src/main/java/gregtech/common/misc/GlobalMetricsCoverDatabase.java new file mode 100644 index 0000000000..33e8198bd6 --- /dev/null +++ b/src/main/java/gregtech/common/misc/GlobalMetricsCoverDatabase.java @@ -0,0 +1,492 @@ +package gregtech.common.misc; + +import static net.minecraftforge.common.util.Constants.NBT.TAG_BYTE_ARRAY; +import static net.minecraftforge.common.util.Constants.NBT.TAG_COMPOUND; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +import java.util.stream.Stream; + +import net.minecraft.entity.item.EntityItem; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagByteArray; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.StatCollector; +import net.minecraft.world.WorldSavedData; +import net.minecraft.world.storage.MapStorage; +import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.entity.item.ItemExpireEvent; +import net.minecraftforge.event.world.BlockEvent; +import net.minecraftforge.event.world.ExplosionEvent.Detonate; +import net.minecraftforge.event.world.WorldEvent; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import gregtech.api.enums.GT_Values; +import gregtech.api.util.GT_Utility; +import gregtech.common.covers.CoverInfo; +import gregtech.common.covers.GT_Cover_Metrics_Transmitter; +import gregtech.common.events.MetricsCoverDataEvent; +import gregtech.common.events.MetricsCoverHostDeconstructedEvent; +import gregtech.common.events.MetricsCoverSelfDestructEvent; + +/** + * Catches and provides data transmitted from deployed Metrics Transmitter covers. Only stores one result per frequency + * at a time. Metrics covers are intended to overwrite an old result every time they emit a new event. + * <br /> + * <br /> + * This information is only partially persisted; frequencies that are in a non-operational state will be written to + * disk, while operational frequencies are volatile. The assumption is that any frequency with a broadcasting card will, + * fairly quickly, re-assert its presence. Conversely, one-time events like deconstruction or self-destruction can occur + * while the card is in a container, rotting on the ground, etc. + */ +public class GlobalMetricsCoverDatabase extends WorldSavedData { + + private static GlobalMetricsCoverDatabase INSTANCE; + + /** Holds received metrics. */ + private static final Map<UUID, Data> DATABASE = new ConcurrentHashMap<>(); + /** Used to speed up event handlers dealing with block breaking and explosions. Not persisted. */ + private static final Map<Coordinates, Set<UUID>> REVERSE_LOOKUP = new ConcurrentHashMap<>(); + + private static final String DATA_NAME = "GregTech_MetricsCoverDatabase"; + private static final String DECONSTRUCTED_KEY = "GregTech_MetricsCoverDatabase_Deconstructed"; + private static final String SELF_DESTRUCTED_KEY = "GregTech_MetricsCoverDatabase_SelfDestructed"; + + public GlobalMetricsCoverDatabase() { + this(DATA_NAME); + } + + public GlobalMetricsCoverDatabase(String name) { + super(name); + } + + @SuppressWarnings("unused") + @SubscribeEvent + public void receiveMetricsData(MetricsCoverDataEvent event) { + final Coordinates coordinates = event.getCoordinates(); + store(event.getFrequency(), State.OPERATIONAL, event.getPayload(), coordinates); + + if (!REVERSE_LOOKUP.containsKey(coordinates)) { + REVERSE_LOOKUP.put(coordinates, new HashSet<>()); + } + REVERSE_LOOKUP.get(coordinates) + .add(event.getFrequency()); + } + + @SuppressWarnings("unused") + @SubscribeEvent + public void receiveHostDeconstructed(MetricsCoverHostDeconstructedEvent event) { + cullReverseLookupEntry(event.getFrequency()); + store(event.getFrequency(), State.HOST_DECONSTRUCTED); + } + + @SuppressWarnings("unused") + @SubscribeEvent + public void receiveSelfDestruct(MetricsCoverSelfDestructEvent event) { + cullReverseLookupEntry(event.getFrequency()); + store(event.getFrequency(), State.SELF_DESTRUCTED); + } + + @SuppressWarnings("unused") + @SubscribeEvent + public void onWorldLoad(WorldEvent.Load event) { + if (event.world.isRemote || event.world.provider.dimensionId != 0) { + return; + } + + DATABASE.clear(); + + final MapStorage storage = event.world.mapStorage; + INSTANCE = (GlobalMetricsCoverDatabase) storage.loadData(GlobalMetricsCoverDatabase.class, DATA_NAME); + if (INSTANCE == null) { + INSTANCE = new GlobalMetricsCoverDatabase(); + storage.setData(DATA_NAME, INSTANCE); + } + + INSTANCE.markDirty(); + } + + @SuppressWarnings("unused") + @SubscribeEvent + public void onBlockBreak(BlockEvent.BreakEvent event) { + final Coordinates coords = new Coordinates(event.world.provider.getDimensionName(), event.x, event.y, event.z); + // In case someone else wants to listen to these, go the roundabout way. + final Set<UUID> uuids = REVERSE_LOOKUP.get(coords); + if (uuids != null) { + uuids.forEach( + uuid -> MinecraftForge.EVENT_BUS.post( + ForgeHooks.canHarvestBlock(event.block, event.getPlayer(), event.blockMetadata) + && !event.getPlayer().capabilities.isCreativeMode ? new MetricsCoverHostDeconstructedEvent(uuid) + : new MetricsCoverSelfDestructEvent(uuid))); + } + } + + @SuppressWarnings("unused") + @SubscribeEvent + public void onExplosion(Detonate event) { + final String dimensionName = event.world.provider.getDimensionName(); + + event.getAffectedBlocks() + .forEach(chunkPosition -> { + final Set<UUID> uuids = REVERSE_LOOKUP.get( + new Coordinates( + dimensionName, + chunkPosition.chunkPosX, + chunkPosition.chunkPosY, + chunkPosition.chunkPosZ)); + + if (uuids != null) { + uuids.forEach(uuid -> MinecraftForge.EVENT_BUS.post(new MetricsCoverSelfDestructEvent(uuid))); + } + }); + + event.getAffectedEntities().forEach(entity -> { + if (entity instanceof final EntityItem entityItem) { + getCoverUUIDsFromItemStack(entityItem.getEntityItem()) + .forEach(uuid -> MinecraftForge.EVENT_BUS.post(new MetricsCoverSelfDestructEvent(uuid))); + } + }); + } + + @SuppressWarnings("unused") + @SubscribeEvent + public void onItemExpiration(ItemExpireEvent event) { + getCoverUUIDsFromItemStack(event.entityItem.getEntityItem()) + .forEach(uuid -> MinecraftForge.EVENT_BUS.post(new MetricsCoverSelfDestructEvent(uuid))); + } + + /** + * Get the data for a frequency, if it exists. + * + * @param frequency The UUID corresponding to the frequency to retrieve. + * @return An Optional with the frequency's data, or an empty Optional if it doesn't exist. + */ + @NotNull + public static Optional<Data> getData(UUID frequency) { + return Optional.ofNullable(DATABASE.get(frequency)); + } + + /** + * Once a card has received the fact that it has self-destructed, this method can be called to free up its spot + * in the database. Does nothing if the frequency is missing or is not in a self-destructed state. + * + * @param frequency The UUID corresponding to the frequency to possibly cull. + */ + public static void clearSelfDestructedFrequency(UUID frequency) { + getData(frequency).ifPresent(data -> { + if (data.getState() == State.SELF_DESTRUCTED) { + DATABASE.remove(frequency); + tryMarkDirty(); + } + }); + } + + @Override + public void readFromNBT(NBTTagCompound nbtTagCompound) { + final NBTTagList deconstructed = nbtTagCompound.getTagList(DECONSTRUCTED_KEY, TAG_BYTE_ARRAY); + final NBTTagList selfDestructed = nbtTagCompound.getTagList(SELF_DESTRUCTED_KEY, TAG_BYTE_ARRAY); + + for (int i = 0; i < deconstructed.tagCount(); i++) { + final NBTTagByteArray byteArray = (NBTTagByteArray) deconstructed.removeTag(0); + reconstituteUUID(byteArray.func_150292_c()) + .ifPresent(uuid -> DATABASE.put(uuid, new Data(State.HOST_DECONSTRUCTED))); + } + + for (int i = 0; i < selfDestructed.tagCount(); i++) { + final NBTTagByteArray byteArray = (NBTTagByteArray) selfDestructed.removeTag(0); + reconstituteUUID(byteArray.func_150292_c()) + .ifPresent(uuid -> DATABASE.put(uuid, new Data(State.SELF_DESTRUCTED))); + } + } + + @Override + public void writeToNBT(NBTTagCompound nbtTagCompound) { + // We only care about persisting frequencies that aren't operational. + final NBTTagList deconstructed = new NBTTagList(); + final NBTTagList selfDestructed = new NBTTagList(); + DATABASE.forEach((uuid, data) -> { + switch (data.getState()) { + case HOST_DECONSTRUCTED -> deconstructed.appendTag(new NBTTagByteArray(dumpUUID(uuid))); + case SELF_DESTRUCTED -> selfDestructed.appendTag(new NBTTagByteArray(dumpUUID(uuid))); + } + }); + + if (deconstructed.tagCount() > 0) { + nbtTagCompound.setTag(DECONSTRUCTED_KEY, deconstructed); + } + if (selfDestructed.tagCount() > 0) { + nbtTagCompound.setTag(SELF_DESTRUCTED_KEY, selfDestructed); + } + } + + /** + * Stores the new result and flag the static {@link MapStorage} instance as dirty if the information updated. Will + * not flag dirty for any data in the {@link State#OPERATIONAL OPERATIONAL} state since they aren't stored. + * + * @param frequency Maps to a unique deployed cover. + * @param state The new cover state. + */ + private static void store(@NotNull UUID frequency, @NotNull State state) { + store(frequency, state, null, null); + } + + /** + * Stores the new result and flag the static {@link MapStorage} instance as dirty if the information updated. Will + * not flag dirty for any data in the {@link State#OPERATIONAL OPERATIONAL} state since they aren't stored. + * + * @param frequency Maps to a unique deployed cover. + * @param state The new cover state. + * @param payload A list of strings to display on the information panel, if the card is slotted properly. + * @param coordinates Coordinates of the active machine (including dimension.) + */ + private static void store(@NotNull UUID frequency, @NotNull State state, @Nullable List<String> payload, + @Nullable Coordinates coordinates) { + final Data newData = new Data(state, payload, coordinates); + final Data oldData = DATABASE.put(frequency, newData); + + if (state != State.OPERATIONAL && (oldData == null || oldData != newData)) { + tryMarkDirty(); + } + } + + private static void tryMarkDirty() { + if (INSTANCE != null) { + INSTANCE.markDirty(); + } + } + + private static byte[] dumpUUID(UUID uuid) { + ByteBuffer buffer = ByteBuffer.wrap(new byte[16]); + buffer.putLong(uuid.getMostSignificantBits()); + buffer.putLong(uuid.getLeastSignificantBits()); + return buffer.array(); + } + + @NotNull + private static Optional<UUID> reconstituteUUID(byte[] bytes) throws IllegalArgumentException { + if (bytes.length != 16) { + return Optional.empty(); + } + + ByteBuffer buffer = ByteBuffer.wrap(bytes); + return Optional.of(new UUID(buffer.getLong(), buffer.getLong())); + } + + private static void cullReverseLookupEntry(UUID frequency) { + getData(frequency).ifPresent(data -> { + if (data.state == State.OPERATIONAL && REVERSE_LOOKUP.containsKey(data.coordinates)) { + final Set<UUID> set = REVERSE_LOOKUP.get(data.coordinates); + set.remove(frequency); + if (set.isEmpty()) { + REVERSE_LOOKUP.remove(data.coordinates); + } + } + }); + } + + private static Stream<UUID> getCoverUUIDsFromItemStack(final ItemStack stack) { + if (stack.hasTagCompound() && stack.getTagCompound() + .hasKey(GT_Values.NBT.COVERS, TAG_COMPOUND)) { + final NBTTagList tagList = stack.getTagCompound() + .getTagList(GT_Values.NBT.COVERS, TAG_COMPOUND); + return IntStream.range(0, tagList.tagCount()) + .mapToObj(tagList::getCompoundTagAt) + .map(nbt -> new CoverInfo(null, nbt).getCoverData()) + .filter( + serializableObject -> serializableObject instanceof GT_Cover_Metrics_Transmitter.MetricsTransmitterData) + .map(data -> ((GT_Cover_Metrics_Transmitter.MetricsTransmitterData) data).getFrequency()); + } + return Stream.empty(); + } + + /** + * Data transmitted by a Metrics Transmitter cover. + * <p> + * Since only negative states ({@link State#HOST_DECONSTRUCTED HOST_DECONSTRUCTED} and + * {@link State#SELF_DESTRUCTED SELF DESTRUCTED}) are persisted, additional fields can be added to this data with + * little consequence. Ensure that any new fields are nullable, and make any getter for these fields return an + * {@link Optional}. + */ + public static class Data { + + @NotNull + private final State state; + @Nullable + private final List<String> payload; + @Nullable + private final Coordinates coordinates; + + public Data(@NotNull State state) { + this.state = state; + this.payload = null; + this.coordinates = null; + } + + public Data(@NotNull State state, @Nullable List<String> payload) { + this.state = state; + this.payload = payload; + this.coordinates = null; + } + + public Data(@NotNull State state, @Nullable List<String> payload, @Nullable Coordinates coordinates) { + this.state = state; + this.payload = payload; + this.coordinates = coordinates; + + } + + /** + * Retrieves the payload for this data. Only present if the frequency is in an + * {@link State#OPERATIONAL operational} state. Will be cleared if the frequency goes into a + * {@link State#HOST_DECONSTRUCTED host-deconstructed} or {@link State#SELF_DESTRUCTED self-destructed} state. + * + * @return The data if present, or an empty Optional otherwise. + */ + @NotNull + public Optional<List<String>> getPayload() { + return Optional.ofNullable(payload); + } + + /** + * Gets the state of the frequency. + * + * @return The state + */ + @NotNull + public State getState() { + return state; + } + + /** + * Gets the last known coordinates for the machine broadcasting metrics. Will only be present in an + * {@link State#OPERATIONAL operational} state. + * + * @return The coordinates + */ + @NotNull + public Optional<Coordinates> getCoordinates() { + return Optional.ofNullable(coordinates); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final Data data = (Data) o; + return state == data.state && Objects.equals(payload, data.payload) + && Objects.equals(coordinates, data.coordinates); + } + + @Override + public int hashCode() { + return Objects.hash(state, payload, coordinates); + } + } + + @SuppressWarnings("ClassCanBeRecord") + public static class Coordinates { + + private final String dimension; + private final int x; + private final int y; + private final int z; + + public Coordinates(final String dimension, final int x, final int y, final int z) { + this.dimension = dimension; + this.x = x; + this.y = y; + this.z = z; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } + + public int getZ() { + return z; + } + + public String getDimension() { + return dimension; + } + + public String getLocalizedCoordinates() { + return StatCollector.translateToLocalFormatted( + "gt.db.metrics_cover.coords", + GT_Utility.formatNumbers(x), + GT_Utility.formatNumbers(y), + GT_Utility.formatNumbers(z)); + } + + @Override + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + final Coordinates that = (Coordinates) o; + return x == that.x && y == that.y && z == that.z && Objects.equals(dimension, that.dimension); + } + + @Override + public int hashCode() { + return Objects.hash(dimension, x, y, z); + } + } + + public enum State { + // NOTE: type cannot be 0, as NuclearControl returns a 0 when querying for an integer from an item stack's NBT + // data when it really means null. + + /** The machine is online and broadcasting metrics. */ + OPERATIONAL(1), + /** + * The machine was picked up, but the cover is still attached. Will transition to operational state if the + * machine is placed back down and started up again. + */ + HOST_DECONSTRUCTED(2), + /** + * Cover was removed from its host machine, or machine was destroyed (in the limited number of ways we can + * detect.) Any frequency in this state will no longer get updates nor leave this state. + */ + SELF_DESTRUCTED(3); + + private static final Map<Integer, State> VALID_TYPE_INTEGERS = Arrays.stream(State.values()) + .collect(Collectors.toMap(State::getType, Function.identity())); + private final int type; + + State(final int type) { + if (type <= 0) { + throw new IllegalArgumentException("A state must have a positive, nonzero type parameter."); + } + this.type = type; + } + + @NotNull + public static Optional<State> find(int candidate) { + return Optional.ofNullable(VALID_TYPE_INTEGERS.get(candidate)); + } + + public int getType() { + return type; + } + } +} diff --git a/src/main/java/gregtech/common/misc/GlobalVariableStorage.java b/src/main/java/gregtech/common/misc/GlobalVariableStorage.java new file mode 100644 index 0000000000..27aad0a11f --- /dev/null +++ b/src/main/java/gregtech/common/misc/GlobalVariableStorage.java @@ -0,0 +1,15 @@ +package gregtech.common.misc; + +import java.math.BigInteger; +import java.util.HashMap; +import java.util.UUID; + +public abstract class GlobalVariableStorage { + // --------------------- NEVER access these maps! Use the methods provided! --------------------- + + // Global EU map. + public static HashMap<UUID, BigInteger> GlobalEnergy = new HashMap<>(100, 0.9f); + + // ---------------------------------------------------------------------------------------------- + +} diff --git a/src/main/java/gregtech/common/misc/WirelessNetworkManager.java b/src/main/java/gregtech/common/misc/WirelessNetworkManager.java new file mode 100644 index 0000000000..17107b4e50 --- /dev/null +++ b/src/main/java/gregtech/common/misc/WirelessNetworkManager.java @@ -0,0 +1,93 @@ +package gregtech.common.misc; + +import static gregtech.common.misc.GlobalVariableStorage.GlobalEnergy; + +import java.math.BigInteger; +import java.util.UUID; + +import gregtech.api.interfaces.tileentity.IGregTechTileEntity; +import gregtech.common.misc.spaceprojects.SpaceProjectManager; + +public class WirelessNetworkManager { + + private WirelessNetworkManager() {} + + public static void strongCheckOrAddUser(UUID user_uuid) { + SpaceProjectManager.checkOrCreateTeam(user_uuid); + if (!GlobalEnergy.containsKey(user_uuid)) { + GlobalEnergy.put(SpaceProjectManager.getLeader(user_uuid), BigInteger.ZERO); + } + } + + // ------------------------------------------------------------------------------------ + // Add EU to the users global energy. You can enter a negative number to subtract it. + // If the value goes below 0 it will return false and not perform the operation. + // BigIntegers have much slower operations than longs/ints. You should call these methods + // as infrequently as possible and bulk store values to add to the global map. + public static boolean addEUToGlobalEnergyMap(UUID user_uuid, BigInteger EU) { + // Mark the data as dirty and in need of saving. + try { + GlobalEnergyWorldSavedData.INSTANCE.markDirty(); + } catch (Exception exception) { + System.out.println("COULD NOT MARK GLOBAL ENERGY AS DIRTY IN ADD EU"); + exception.printStackTrace(); + } + + // Get the team UUID. Users are by default in a team with a UUID equal to their player UUID. + UUID teamUUID = SpaceProjectManager.getLeader(user_uuid); + + // Get the teams total energy stored. If they are not in the map, return 0 EU. + BigInteger totalEU = GlobalEnergy.getOrDefault(teamUUID, BigInteger.ZERO); + totalEU = totalEU.add(EU); + + // If there is sufficient EU then complete the operation and return true. + if (totalEU.signum() >= 0) { + GlobalEnergy.put(teamUUID, totalEU); + return true; + } + + // There is insufficient EU so cancel the operation and return false. + return false; + } + + public static boolean addEUToGlobalEnergyMap(UUID user_uuid, long EU) { + return addEUToGlobalEnergyMap(user_uuid, BigInteger.valueOf(EU)); + } + + public static boolean addEUToGlobalEnergyMap(UUID user_uuid, int EU) { + return addEUToGlobalEnergyMap(user_uuid, BigInteger.valueOf(EU)); + } + + // ------------------------------------------------------------------------------------ + + public static BigInteger getUserEU(UUID user_uuid) { + return GlobalEnergy.getOrDefault(SpaceProjectManager.getLeader(user_uuid), BigInteger.ZERO); + } + + // This overwrites the EU in the network. Only use this if you are absolutely sure you know what you are doing. + public static void setUserEU(UUID user_uuid, BigInteger EU) { + // Mark the data as dirty and in need of saving. + try { + GlobalEnergyWorldSavedData.INSTANCE.markDirty(); + } catch (Exception exception) { + System.out.println("COULD NOT MARK GLOBAL ENERGY AS DIRTY IN SET EU"); + exception.printStackTrace(); + } + + GlobalEnergy.put(SpaceProjectManager.getLeader(user_uuid), EU); + } + + public static void clearGlobalEnergyInformationMaps() { + // Do not use this unless you are 100% certain you know what you are doing. + GlobalEnergy.clear(); + } + + public static UUID processInitialSettings(final IGregTechTileEntity machine) { + + // UUID and username of the owner. + final UUID UUID = machine.getOwnerUuid(); + + SpaceProjectManager.checkOrCreateTeam(UUID); + return UUID; + } +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/SpaceProjectManager.java b/src/main/java/gregtech/common/misc/spaceprojects/SpaceProjectManager.java new file mode 100644 index 0000000000..323b22e20a --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/SpaceProjectManager.java @@ -0,0 +1,309 @@ +package gregtech.common.misc.spaceprojects; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import net.minecraft.item.ItemStack; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.fluids.FluidStack; + +import org.apache.commons.lang3.tuple.Pair; + +import gregtech.api.recipe.RecipeMaps; +import gregtech.api.util.GT_Recipe; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceBody; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceProject; + +/** + * @author BlueWeabo + */ +public class SpaceProjectManager { + + /** + * Do not use! Only meant to be used in SpaceProjectWorldSavedData.java + */ + public static Map<UUID, Map<Pair<ISpaceBody, String>, ISpaceProject>> spaceTeamProjects = new HashMap<>(); + /** + * Do not use! Only meant to be used in SpaceProjectWorldSavedData.java Stores a Players UUID to the Leader UUID, + * players in lone groups give back their own UUID. + */ + public static Map<UUID, UUID> spaceTeams = new HashMap<>(); + + /** + * Stores all the locations to a hash map to be accessed easier instead of through an enum + */ + private static final HashMap<String, ISpaceBody> spaceLocations = new HashMap<>(); + + /** + * Stores all projects that have been made. Only adds them to this map if {@link #addProject(ISpaceProject)} has + * been used + */ + private static final Map<String, ISpaceProject> spaceProjects = new HashMap<>(); + + // #region Space Project Team Helper methods + + /** + * Used to get a specific project of the team dependent on the location and the project's name + */ + public static ISpaceProject getTeamProject(UUID member, ISpaceBody location, String projectName) { + Map<Pair<ISpaceBody, String>, ISpaceProject> map = spaceTeamProjects.get(getLeader(member)); + if (map == null) { + return null; + } + return map.get(Pair.of(location, projectName)); + } + + /** + * Makes a new Map for the teams if they don't have one. Adds a project to the team's project map. + * + * @param member Member of the team. + * @param location The location of where the project will belong to. + * @param projectName The name of the project being added. + * @param project Project which will be added to the team. + * @return Returns true when a project was added to the map of the player. Returns false otherwise. + */ + public static boolean addTeamProject(UUID member, ISpaceBody location, String projectName, ISpaceProject project) { + if (!spaceTeamProjects.containsKey(getLeader(member)) || spaceTeamProjects.get(getLeader(member)) == null) { + spaceTeamProjects.put(getLeader(member), new HashMap<>()); + } + + Map<Pair<ISpaceBody, String>, ISpaceProject> map = spaceTeamProjects.get(getLeader(member)); + if (map.containsKey(Pair.of(location, projectName))) { + return false; + } + + project.setProjectLocation(location); + map.put(Pair.of(location, projectName), project); + if (SpaceProjectWorldSavedData.INSTANCE != null) { + SpaceProjectWorldSavedData.INSTANCE.markDirty(); + } + return true; + } + + /** + * Check whether a team has a project or not + * + * @param member Member of the team + * @param project The project, which you are checking for. This only compares the internal names of the project. + * @return True if the team has said project, false otherwise + */ + public static boolean teamHasProject(UUID member, ISpaceProject project) { + Map<Pair<ISpaceBody, String>, ISpaceProject> map = spaceTeamProjects.get(getLeader(member)); + if (map == null) { + return false; + } + + return map.containsValue(project); + } + + /** + * Used to handle when 2 players want to join together in a team. Player A can join player B's team. If player C + * gets an invite from A, C will join player B's team. + * + * @param teamMember Member which is joining the teamLeader + * @param teamLeader Leader of the party + */ + public static void putInTeam(UUID teamMember, UUID teamLeader) { + if (teamMember.equals(teamLeader)) { + spaceTeams.put(teamMember, teamLeader); + } else if (!spaceTeams.get(teamLeader) + .equals(teamLeader)) { + putInTeam(teamMember, spaceTeams.get(teamLeader)); + } else { + spaceTeams.put(teamMember, teamLeader); + } + + if (SpaceProjectWorldSavedData.INSTANCE != null) { + SpaceProjectWorldSavedData.INSTANCE.markDirty(); + } + } + + /** + * Used to give back the UUID of the team leader. + * + * @return The UUID of the team leader. + */ + public static UUID getLeader(UUID teamMember) { + checkOrCreateTeam(teamMember); + return spaceTeams.get(teamMember); + } + + /** + * Used the multiblocks to check whether a given player has a team or not. If they don't have a team create one + * where they are their own leader. + * + * @param teamMember Member to check for. + */ + public static void checkOrCreateTeam(UUID teamMember) { + if (spaceTeams.containsKey(teamMember)) { + return; + } + + spaceTeams.put(teamMember, teamMember); + if (SpaceProjectWorldSavedData.INSTANCE != null) { + SpaceProjectWorldSavedData.INSTANCE.markDirty(); + } + } + + public static boolean isInTeam(UUID member) { + return spaceTeams.containsKey(member); + } + + /** + * Will give back all the projects a team has made or is making. + * + * @param member UUID of the team member, used to find the leader of the team. + * @return All the projects a team has. + */ + public static Collection<ISpaceProject> getTeamSpaceProjects(UUID member) { + Map<Pair<ISpaceBody, String>, ISpaceProject> map = spaceTeamProjects.get(getLeader(member)); + if (map == null) { + return null; + } + + return map.values(); + } + + /** + * Getting the project of a Team or a new copy. + * + * @param member UUID of the team member, which is used to find the team leader. + * @param projectName The name of the project, which needs to be found. + * @param location The location at which the project is found at. + * @return the project that the team has or a copy of a project. + */ + public static ISpaceProject getTeamProjectOrCopy(UUID member, String projectName, ISpaceBody location) { + Map<Pair<ISpaceBody, String>, ISpaceProject> map = spaceTeamProjects.get(getLeader(member)); + if (map == null) { + return getProject(projectName); + } + + return map.getOrDefault(Pair.of(location, projectName), getProject(projectName)); + } + + // #endregion + + // #region Project Helper methods + + public static class FakeSpaceProjectRecipe extends GT_Recipe { + + public final String projectName; + + public FakeSpaceProjectRecipe(boolean aOptimize, ItemStack[] aInputs, FluidStack[] aFluidInputs, int aDuration, + int aEUt, int aSpecialValue, String projectName) { + super(aOptimize, aInputs, null, null, null, aFluidInputs, null, aDuration, aEUt, aSpecialValue); + this.projectName = projectName; + } + } + + /** + * Used to add projects to the internal map. + * + * @param project Newly created project. + */ + public static void addProject(ISpaceProject project) { + spaceProjects.put(project.getProjectName(), project); + RecipeMaps.spaceProjectFakeRecipes.add( + new FakeSpaceProjectRecipe( + false, + project.getTotalItemsCost(), + project.getTotalFluidsCost(), + project.getProjectBuildTime(), + (int) project.getProjectVoltage(), + project.getTotalStages(), + project.getProjectName())); + } + + /** + * @param projectName Internal name of the project. + * @return a copy of the stored project. + */ + public static ISpaceProject getProject(String projectName) { + ISpaceProject tProject = spaceProjects.get(projectName); + return tProject != null ? tProject.copy() : null; + } + + /** + * Should only be used for GUIs! + * + * @return The Map that the projects are stored at. + */ + public static Map<String, ISpaceProject> getProjectsMap() { + return spaceProjects; + } + + /** + * Should only be used for GUIs! + * + * @return A Collection of all the projects contained in the map. + */ + public static Collection<ISpaceProject> getAllProjects() { + return spaceProjects.values(); + } + + // #endregion + + // #region Location Helper methods + + /** + * Adds a location to the internal map. For it to be used later + * + * @param location to add to the internal map + */ + public static void addLocation(ISpaceBody location) { + spaceLocations.put(location.getName(), location); + } + + /** + * + * @return a Collection of all locations, which have been registered. + */ + public static Collection<ISpaceBody> getLocations() { + return spaceLocations.values(); + } + + /** + * + * @return a Collection fo all location names, which have been registered + */ + public static Collection<String> getLocationNames() { + return spaceLocations.keySet(); + } + + /** + * + * @param locationName Name used to search for the location + * @return The location, which has been registered with said name + */ + public static ISpaceBody getLocation(String locationName) { + return spaceLocations.get(locationName); + } + + // #endregion + + // #region General Helper methods + + /** + * Gets the UUID using the player's username + */ + public static UUID getPlayerUUIDFromName(String playerName) { + return MinecraftServer.getServer() + .func_152358_ax() + .func_152655_a(playerName) + .getId(); + } + + /** + * Gets the player's name using their UUID + */ + public static String getPlayerNameFromUUID(UUID playerUUID) { + return MinecraftServer.getServer() + .func_152358_ax() + .func_152652_a(playerUUID) + .getName(); + } + + // #endregion +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/SpaceProjectWorldSavedData.java b/src/main/java/gregtech/common/misc/spaceprojects/SpaceProjectWorldSavedData.java new file mode 100644 index 0000000000..12f0005aea --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/SpaceProjectWorldSavedData.java @@ -0,0 +1,343 @@ +package gregtech.common.misc.spaceprojects; + +import static gregtech.common.misc.spaceprojects.SpaceProjectManager.spaceTeamProjects; +import static gregtech.common.misc.spaceprojects.SpaceProjectManager.spaceTeams; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.MAP_MAP; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.MAP_PAIR; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.MAP_PROJECT; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.MAP_UUID; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.PAIR_LEFT; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.PAIR_RIGHT; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.PROJECT_CURRENT_STAGE; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.PROJECT_CURRENT_UPGRADE; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.PROJECT_LOCATION; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.PROJECT_NAME; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.PROJECT_UPGRADES_BUILT; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.UPGRADE_CURRENT_STAGE; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.UPGRADE_NAME; +import static gregtech.common.misc.spaceprojects.enums.JsonVariables.UPGRADE_PROJECT_PARENT; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.world.World; +import net.minecraft.world.WorldSavedData; +import net.minecraft.world.storage.MapStorage; +import net.minecraftforge.event.world.WorldEvent; + +import org.apache.commons.lang3.tuple.Pair; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; + +import cpw.mods.fml.common.eventhandler.SubscribeEvent; +import gregtech.common.misc.spaceprojects.enums.SolarSystem; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceBody; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceProject; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceProject.ISP_Upgrade; + +/** + * This class is used so that I can write and read to a json file before the world is opened. On server starting is too + * late for this as the data stored in the files is needed before entities load their nbt data + * + * @author BlueWeabo + */ +public class SpaceProjectWorldSavedData extends WorldSavedData { + + public static SpaceProjectWorldSavedData INSTANCE; + + private static final Gson GSON_SPACE_PROJECT = new GsonBuilder().serializeNulls() + .enableComplexMapKeySerialization() + .registerTypeAdapter(spaceTeamProjects.getClass(), new SpaceTeamProjectsMapAdapter()) + .registerTypeAdapter(Map.class, new SpaceTeamProjectsMapAdapter()) + .registerTypeAdapter( + Pair.of((ISpaceBody) SolarSystem.Ariel, "") + .getClass(), + new PairAdapter()) + .registerTypeAdapter(Pair.class, new PairAdapter()) + .registerTypeAdapter(ISpaceProject.class, new SpaceProjectAdapter()) + .registerTypeAdapter(ISP_Upgrade.class, new SP_UpgradeAdapter()) + .registerTypeHierarchyAdapter(ISpaceProject.class, new SpaceProjectAdapter()) + .registerTypeHierarchyAdapter(ISP_Upgrade.class, new SP_UpgradeAdapter()) + .create(); + private static final Gson GSON_TEAMS = new GsonBuilder().serializeNulls() + .create(); + + private static final String DATA_NAME = "GT_SpaceProjectData"; + + private static final String SPACE_TEAM_PROJECTS_JSON = "spaceTeamProject.json"; + + private static final String SPACE_TEAMS_JSON = "spaceTeams.json"; + + private static File spaceTeamsFile; + private static File teamProjectsFile; + + public SpaceProjectWorldSavedData() { + super(DATA_NAME); + } + + public SpaceProjectWorldSavedData(String aData) { + super(aData); + } + + @Override + public void readFromNBT(NBTTagCompound aNBT) { + try (JsonReader reader = new JsonReader(new FileReader(teamProjectsFile))) { + spaceTeamProjects = GSON_SPACE_PROJECT.fromJson(reader, spaceTeamProjects.getClass()); + } catch (Exception e) { + System.out.print("FAILED TO LOAD: " + SPACE_TEAM_PROJECTS_JSON); + e.printStackTrace(); + } + + try (JsonReader reader = new JsonReader(new FileReader(spaceTeamsFile))) { + HashMap<UUID, UUID> jsonMap = GSON_TEAMS.fromJson(reader, spaceTeams.getClass()); + for (UUID member : jsonMap.keySet()) { + spaceTeams.put(member, jsonMap.get(member)); + } + } catch (Exception e) { + System.out.print("FAILED TO LOAD: " + SPACE_TEAMS_JSON); + e.printStackTrace(); + } + + if (spaceTeamProjects == null) { + spaceTeamProjects = new HashMap<>(); + } + + if (spaceTeams == null) { + spaceTeams = new HashMap<>(); + } + } + + @Override + public void writeToNBT(NBTTagCompound aNBT) { + if (spaceTeamProjects != null) { + try (JsonWriter writer = new JsonWriter(new FileWriter(teamProjectsFile))) { + GSON_SPACE_PROJECT.toJson(spaceTeamProjects, spaceTeamProjects.getClass(), writer); + } catch (Exception ex) { + System.out.print("FAILED TO SAVE: " + SPACE_TEAM_PROJECTS_JSON); + ex.printStackTrace(); + } + } + + if (spaceTeams != null) { + try (JsonWriter writer = new JsonWriter(new FileWriter(spaceTeamsFile))) { + GSON_TEAMS.toJson(spaceTeams, spaceTeams.getClass(), writer); + } catch (Exception ex) { + System.out.print("FAILED TO SAVE: " + SPACE_TEAMS_JSON); + ex.printStackTrace(); + } + } + } + + private static void loadInstance(World aWorld) { + if (spaceTeamProjects != null) { + spaceTeamProjects.clear(); + } else { + spaceTeamProjects = new HashMap<>(); + } + if (spaceTeams != null) { + spaceTeams.clear(); + } else { + spaceTeams = new HashMap<>(); + } + spaceTeamsFile = new File( + aWorld.getSaveHandler() + .getWorldDirectory(), + SPACE_TEAMS_JSON); + teamProjectsFile = new File( + aWorld.getSaveHandler() + .getWorldDirectory(), + SPACE_TEAM_PROJECTS_JSON); + MapStorage tStorage = aWorld.mapStorage; + INSTANCE = (SpaceProjectWorldSavedData) tStorage.loadData(SpaceProjectWorldSavedData.class, DATA_NAME); + if (INSTANCE == null) { + INSTANCE = new SpaceProjectWorldSavedData(); + tStorage.setData(DATA_NAME, INSTANCE); + } + INSTANCE.markDirty(); + } + + @SubscribeEvent + public void onWorldLoad(WorldEvent.Load aEvent) { + if (!aEvent.world.isRemote && aEvent.world.provider.dimensionId == 0) { + loadInstance(aEvent.world); + } + } + + private static class PairAdapter + implements JsonSerializer<Pair<ISpaceBody, String>>, JsonDeserializer<Pair<ISpaceBody, String>> { + + @Override + public JsonElement serialize(Pair<ISpaceBody, String> src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject pair = new JsonObject(); + pair.addProperty( + PAIR_LEFT, + src.getLeft() + .getName()); + pair.addProperty(PAIR_RIGHT, src.getRight()); + return pair; + } + + @Override + public Pair<ISpaceBody, String> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + Pair<ISpaceBody, String> pair = null; + if (json.isJsonObject()) { + JsonObject obj = json.getAsJsonObject(); + pair = Pair.of( + SpaceProjectManager.getLocation( + obj.get(PAIR_LEFT) + .getAsString()), + obj.get(PAIR_RIGHT) + .getAsString()); + } + return pair; + } + } + + private static class SpaceProjectAdapter implements JsonSerializer<ISpaceProject>, JsonDeserializer<ISpaceProject> { + + @Override + public JsonElement serialize(ISpaceProject src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject obj = new JsonObject(); + obj.addProperty(PROJECT_NAME, src.getProjectName()); + obj.addProperty(PROJECT_CURRENT_STAGE, src.getCurrentStage()); + obj.addProperty( + PROJECT_LOCATION, + src.getProjectLocation() + .getName()); + obj.add(PROJECT_CURRENT_UPGRADE, context.serialize(src.getUpgradeBeingBuilt())); + obj.add(PROJECT_UPGRADES_BUILT, context.serialize(src.getAllBuiltUpgrades())); + return obj; + } + + @Override + public ISpaceProject deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + if (!json.isJsonObject()) { + return null; + } + JsonObject obj = json.getAsJsonObject(); + String projectName = obj.get(PROJECT_NAME) + .getAsString(); + ISpaceProject project = SpaceProjectManager.getProject(projectName); + int projectCurrentStage = obj.get(PROJECT_CURRENT_STAGE) + .getAsInt(); + ISP_Upgrade[] projectUpgradesBuilt = new ISP_Upgrade[0]; + projectUpgradesBuilt = context + .deserialize(obj.get(PROJECT_UPGRADES_BUILT), projectUpgradesBuilt.getClass()); + ISP_Upgrade projectCurrentUpgrade = context + .deserialize(obj.get(PROJECT_CURRENT_UPGRADE), ISP_Upgrade.class); + ISpaceBody projectLocation = SpaceProjectManager.getLocation( + obj.get(PROJECT_LOCATION) + .getAsString()); + project.setBuiltUpgrade(projectUpgradesBuilt); + project.setProjectLocation(projectLocation); + project.setProjectCurrentStage(projectCurrentStage); + project.setCurrentUpgradeBeingBuilt(projectCurrentUpgrade); + return project; + } + } + + private static class SP_UpgradeAdapter implements JsonSerializer<ISP_Upgrade>, JsonDeserializer<ISP_Upgrade> { + + @Override + public JsonElement serialize(ISP_Upgrade src, Type typeOfSrc, JsonSerializationContext context) { + JsonObject obj = new JsonObject(); + obj.addProperty(UPGRADE_NAME, src.getUpgradeName()); + obj.addProperty( + UPGRADE_PROJECT_PARENT, + src.getParentProject() + .getProjectName()); + obj.addProperty(UPGRADE_CURRENT_STAGE, src.getCurrentStage()); + return obj; + } + + @Override + public ISP_Upgrade deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + if (!json.isJsonObject()) { + return null; + } + JsonObject obj = json.getAsJsonObject(); + String projectName = obj.get(UPGRADE_PROJECT_PARENT) + .getAsString(); + ISpaceProject project = SpaceProjectManager.getProject(projectName); + ISP_Upgrade upgrade = project.getUpgrade( + obj.get(UPGRADE_NAME) + .getAsString()); + if (upgrade == null) { + return null; + } + upgrade = upgrade.copy(); + upgrade.setUpgradeCurrentStage( + obj.get(UPGRADE_CURRENT_STAGE) + .getAsInt()); + return upgrade; + } + } + + private static class SpaceTeamProjectsMapAdapter + implements JsonSerializer<Map<UUID, Map<Pair<ISpaceBody, String>, ISpaceProject>>>, + JsonDeserializer<Map<UUID, Map<Pair<ISpaceBody, String>, ISpaceProject>>> { + + @Override + public JsonElement serialize(Map<UUID, Map<Pair<ISpaceBody, String>, ISpaceProject>> src, Type typeOfSrc, + JsonSerializationContext context) { + JsonArray map = new JsonArray(); + for (Entry<UUID, Map<Pair<ISpaceBody, String>, ISpaceProject>> firstEntry : src.entrySet()) { + JsonObject teamMap = new JsonObject(); + teamMap.add(MAP_UUID, context.serialize(firstEntry.getKey())); + JsonArray teamProjectMap = new JsonArray(); + for (Entry<Pair<ISpaceBody, String>, ISpaceProject> secondEntry : firstEntry.getValue() + .entrySet()) { + JsonObject projectMap = new JsonObject(); + projectMap.add(MAP_PAIR, context.serialize(secondEntry.getKey())); + projectMap.add(MAP_PROJECT, context.serialize(secondEntry.getValue())); + teamProjectMap.add(projectMap); + } + teamMap.add(MAP_MAP, teamProjectMap); + map.add(teamMap); + } + return map; + } + + @Override + public Map<UUID, Map<Pair<ISpaceBody, String>, ISpaceProject>> deserialize(JsonElement json, Type typeOfT, + JsonDeserializationContext context) throws JsonParseException { + JsonArray mapArray = json.getAsJsonArray(); + Map<UUID, Map<Pair<ISpaceBody, String>, ISpaceProject>> map = new HashMap<>(); + for (JsonElement teamMapElement : mapArray) { + JsonObject teamMap = teamMapElement.getAsJsonObject(); + UUID uuid = context.deserialize(teamMap.get(MAP_UUID), UUID.class); + Map<Pair<ISpaceBody, String>, ISpaceProject> projectMap = new HashMap<>(); + for (JsonElement teamProjectMapElement : teamMap.get(MAP_MAP) + .getAsJsonArray()) { + JsonObject teamProjectMap = teamProjectMapElement.getAsJsonObject(); + Pair<ISpaceBody, String> pair = context.deserialize(teamProjectMap.get(MAP_PAIR), Pair.class); + ISpaceProject project = context.deserialize(teamProjectMap.get(MAP_PROJECT), ISpaceProject.class); + projectMap.put(pair, project); + } + map.put(uuid, projectMap); + } + return map; + } + } +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/base/SP_Requirements.java b/src/main/java/gregtech/common/misc/spaceprojects/base/SP_Requirements.java new file mode 100644 index 0000000000..b910b5e344 --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/base/SP_Requirements.java @@ -0,0 +1,74 @@ +package gregtech.common.misc.spaceprojects.base; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import gregtech.common.misc.spaceprojects.enums.SpaceBodyType; +import gregtech.common.misc.spaceprojects.enums.StarType; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceProject; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceProject.ISP_Requirements; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceProject.ISP_Upgrade; + +/** + * @author BlueWeabo + */ +public class SP_Requirements implements ISP_Requirements { + + // #region Variables + + protected SpaceBodyType spaceBody = SpaceBodyType.NONE; + protected StarType star = StarType.NotAStar; + protected List<ISpaceProject> spaceProjects = new ArrayList<>(); + protected List<ISP_Upgrade> upgrades = new ArrayList<>(); + + // #endregion + + // #region Getters + + @Override + public SpaceBodyType getBodyType() { + return spaceBody; + } + + @Override + public StarType getStarType() { + return star; + } + + @Override + public List<ISpaceProject> getProjects() { + return spaceProjects; + } + + @Override + public List<ISP_Upgrade> getUpgrades() { + return upgrades; + } + + // #endregion + + // #region Setters/Builder + + public SP_Requirements setSpaceBodyType(SpaceBodyType spaceBodyType) { + spaceBody = spaceBodyType; + return this; + } + + public SP_Requirements setStarType(StarType starType) { + star = starType; + return this; + } + + public SP_Requirements setUpgrades(ISP_Upgrade... requirementUpgrades) { + upgrades.addAll(Arrays.asList(requirementUpgrades)); + return this; + } + + public SP_Requirements setSpaceProjects(ISpaceProject... requirementProjects) { + spaceProjects.addAll(Arrays.asList(requirementProjects)); + return this; + } + + // #endregion +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/base/SP_Upgrade.java b/src/main/java/gregtech/common/misc/spaceprojects/base/SP_Upgrade.java new file mode 100644 index 0000000000..835a57f277 --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/base/SP_Upgrade.java @@ -0,0 +1,362 @@ +package gregtech.common.misc.spaceprojects.base; + +import java.util.UUID; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.StatCollector; +import net.minecraftforge.fluids.FluidStack; + +import gregtech.common.misc.spaceprojects.SpaceProjectManager; +import gregtech.common.misc.spaceprojects.enums.SpaceBodyType; +import gregtech.common.misc.spaceprojects.enums.UpgradeStatus; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceProject; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceProject.ISP_Upgrade; + +/** + * @author BlueWeabo + */ +public class SP_Upgrade implements ISP_Upgrade { + + // #region Variables + + protected String name; + protected String unlocalizedName; + protected ItemStack[] itemsCost; + protected FluidStack[] fluidsCost; + protected int totalStages; + protected int currentStage; + protected int buildTime; + protected long voltage; + protected SP_Requirements requirements; + protected ISpaceProject projectBelongingTo; + + // #endregion + + // #region Getters + + @Override + public String getUpgradeName() { + return name; + } + + @Override + public String getUnlocalizedName() { + return unlocalizedName; + } + + @Override + public String getLocalizedName() { + return StatCollector.translateToLocal(unlocalizedName); + } + + @Override + public ItemStack[] getItemsCostPerStage() { + return itemsCost; + } + + @Override + public ItemStack getItemCostPerStage(int index) { + if (itemsCost == null || index < 0 || index >= itemsCost.length) { + return null; + } + + return itemsCost[index]; + } + + @Override + public ItemStack[] getCurrentItemsProgress() { + ItemStack[] currentItemsProgress = new ItemStack[itemsCost.length]; + int index = 0; + for (ItemStack item : itemsCost) { + ItemStack copy = item.copy(); + copy.stackSize *= getCurrentStage(); + currentItemsProgress[index++] = copy; + } + + return currentItemsProgress; + } + + @Override + public ItemStack getCurrentItemProgress(int index) { + if (itemsCost == null || index < 0 || index >= itemsCost.length || itemsCost[index] == null) { + return null; + } + + ItemStack item = itemsCost[index].copy(); + item.stackSize *= getCurrentStage(); + return item; + } + + @Override + public ItemStack[] getTotalItemsCost() { + ItemStack[] totalItemsCost = new ItemStack[itemsCost.length]; + int index = 0; + for (ItemStack item : itemsCost) { + ItemStack copy = item.copy(); + copy.stackSize *= getTotalStages(); + totalItemsCost[index++] = copy; + } + + return totalItemsCost; + } + + @Override + public ItemStack getTotalItemCost(int index) { + if (itemsCost == null || index < 0 || index >= itemsCost.length || itemsCost[index] == null) { + return null; + } + + ItemStack item = itemsCost[index].copy(); + item.stackSize *= getTotalStages(); + return item; + } + + @Override + public FluidStack[] getFluidsCostPerStage() { + return fluidsCost; + } + + @Override + public FluidStack getFluidCostPerStage(int index) { + if (fluidsCost == null || index < 0 || index >= fluidsCost.length) { + return null; + } + + return fluidsCost[index]; + } + + @Override + public FluidStack[] getCurrentFluidsProgress() { + if (fluidsCost == null) { + return null; + } + + FluidStack[] currentFluidsProgress = new FluidStack[fluidsCost.length]; + int index = 0; + for (FluidStack fluid : fluidsCost) { + FluidStack copy = fluid.copy(); + copy.amount *= getCurrentStage(); + currentFluidsProgress[index++] = copy; + } + + return currentFluidsProgress; + } + + @Override + public FluidStack getCurrentFluidProgress(int index) { + if (fluidsCost == null || index < 0 || index >= fluidsCost.length || fluidsCost[index] == null) { + return null; + } + + FluidStack fluid = fluidsCost[index].copy(); + fluid.amount *= getCurrentStage(); + return fluid; + } + + @Override + public FluidStack[] getTotalFluidsCost() { + if (fluidsCost == null) { + return null; + } + + FluidStack[] totalFluidsCost = new FluidStack[fluidsCost.length]; + int index = 0; + for (FluidStack fluid : fluidsCost) { + FluidStack copy = fluid.copy(); + copy.amount *= getTotalStages(); + totalFluidsCost[index++] = copy; + } + + return totalFluidsCost; + } + + @Override + public FluidStack getTotalFluidCost(int index) { + if (fluidsCost == null || index < 0 || index >= fluidsCost.length || fluidsCost[index] == null) { + return null; + } + + FluidStack fluid = fluidsCost[index].copy(); + fluid.amount *= getTotalStages(); + return fluid; + } + + @Override + public int getTotalStages() { + return totalStages; + } + + @Override + public int getUpgradeBuildTime() { + return buildTime; + } + + @Override + public int getCurrentStage() { + return currentStage; + } + + @Override + public float getCurrentProgress() { + return currentStage / totalStages * 100.0f; + } + + @Override + public long getVoltage() { + return voltage; + } + + @Override + public UpgradeStatus getStatus() { + if (requirements == null) { + return UpgradeStatus.Unlocked; + } + + if (isFinished()) { + return UpgradeStatus.Finished; + } + return UpgradeStatus.Locked; + } + + @Override + public SP_Requirements getUpgradeRequirements() { + return requirements; + } + + @Override + public ISpaceProject getParentProject() { + return projectBelongingTo; + } + + // #endregion + + // #region Setter/Builder + + public SP_Upgrade() {} + + public SP_Upgrade setUpgradeName(String upgradeName) { + name = upgradeName; + return this; + } + + public SP_Upgrade setUpgradeUnlocalizedName(String upgradeUnlocalizedName) { + unlocalizedName = upgradeUnlocalizedName; + return this; + } + + public SP_Upgrade setUpgradeItemsCost(ItemStack... upgradeItemsCost) { + itemsCost = upgradeItemsCost; + return this; + } + + public SP_Upgrade setUpgradeFluidsCost(FluidStack... upgradeFluidsCost) { + fluidsCost = upgradeFluidsCost; + return this; + } + + public SP_Upgrade setUpgradeRequirements(SP_Requirements upgradeRequirements) { + requirements = upgradeRequirements; + return this; + } + + public SP_Upgrade setUpgradeTotalStages(int upgradeTotalStages) { + totalStages = upgradeTotalStages; + return this; + } + + public SP_Upgrade setUpgradeBuildTime(int upgradeBuildTime) { + buildTime = upgradeBuildTime; + return this; + } + + public SP_Upgrade setUpgradeVoltage(long upgradeVoltage) { + voltage = upgradeVoltage; + return this; + } + + @Override + public void setUpgradeProject(ISpaceProject project) { + projectBelongingTo = project; + } + + @Override + public void setUpgradeCurrentStage(int stage) { + currentStage = stage; + } + + // #endregion + + // #region Other + + @Override + public boolean meetsRequirements(UUID aTeam) { + if (requirements == null) { + return true; + } + + if (requirements.getBodyType() != null && !requirements.getBodyType() + .equals(SpaceBodyType.NONE)) { + if (!requirements.getBodyType() + .equals( + projectBelongingTo.getProjectLocation() + .getType())) { + return false; + } + } + + if (requirements.getStarType() != null) { + if (!requirements.getStarType() + .equals( + projectBelongingTo.getProjectLocation() + .getStarType())) { + return false; + } + } + + if (requirements.getProjects() != null) { + for (ISpaceProject tProject : requirements.getProjects()) { + if (!SpaceProjectManager.teamHasProject(aTeam, tProject)) { + return false; + } + } + } + + if (requirements.getUpgrades() != null) { + for (ISP_Upgrade upgrade : requirements.getUpgrades()) { + if (!projectBelongingTo.hasUpgrade(upgrade.getUpgradeName())) { + return false; + } + } + } + + return true; + } + + @Override + public SP_Upgrade copy() { + return new SP_Upgrade().setUpgradeName(name) + .setUpgradeUnlocalizedName(unlocalizedName) + .setUpgradeBuildTime(buildTime) + .setUpgradeFluidsCost(fluidsCost) + .setUpgradeItemsCost(itemsCost) + .setUpgradeRequirements(requirements) + .setUpgradeTotalStages(totalStages) + .setUpgradeVoltage(voltage); + } + + @Override + public void goToNextStage() { + currentStage++; + if (isFinished()) { + projectBelongingTo.setBuiltUpgrade(this); + } + } + + @Override + public boolean isFinished() { + return currentStage == totalStages; + } + + // #endregion + +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/base/SpaceProject.java b/src/main/java/gregtech/common/misc/spaceprojects/base/SpaceProject.java new file mode 100644 index 0000000000..201b7c27a9 --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/base/SpaceProject.java @@ -0,0 +1,451 @@ +package gregtech.common.misc.spaceprojects.base; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.StatCollector; +import net.minecraftforge.fluids.FluidStack; + +import com.gtnewhorizons.modularui.api.drawable.UITexture; + +import gregtech.common.misc.spaceprojects.SpaceProjectManager; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceBody; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceProject; + +/** + * @author BlueWeabo + */ +public class SpaceProject implements ISpaceProject { + + // #region Variables + + protected String name; + protected String unlocalizedName; + protected long voltage; + protected int buildTime; + protected int projectTier; + protected int currentStage; + protected int totalStage; + protected Map<String, ISP_Upgrade> upgradesAvailable = new HashMap<>(); + protected Map<String, ISP_Upgrade> upgradesInstalled = new HashMap<>(); + protected ISP_Requirements requirements; + protected ISP_Upgrade currentUpgrade; + protected ItemStack[] itemsCost; + protected FluidStack[] fluidsCost; + protected ISpaceBody location; + protected UITexture texture; + + // #endregion + + // #region Getters + + @Override + public String getProjectName() { + return name; + } + + @Override + public String getUnlocalizedName() { + return unlocalizedName; + } + + public String getLocalizedName() { + return StatCollector.translateToLocal(unlocalizedName); + } + + @Override + public long getProjectVoltage() { + return voltage; + } + + @Override + public int getProjectBuildTime() { + return buildTime; + } + + @Override + public float getProjectCurrentProgress() { + return currentStage / totalStage * 100.0f; + } + + @Override + public int getProjectTier() { + return projectTier; + } + + @Override + public int getCurrentStage() { + return currentStage; + } + + @Override + public int getTotalStages() { + return totalStage; + } + + @Override + public Collection<ISP_Upgrade> getAllUpgrades() { + return upgradesAvailable.values(); + } + + @Override + public Map<String, ISP_Upgrade> getUpgradesBuiltMap() { + return upgradesInstalled; + } + + @Override + public Collection<ISP_Upgrade> getAllBuiltUpgrades() { + return upgradesInstalled.values(); + } + + @Override + public ISP_Upgrade getUpgrade(String upgradeName) { + return upgradesAvailable.get(upgradeName); + } + + @Override + public ItemStack[] getItemsCostPerStage() { + return itemsCost; + } + + @Override + public ItemStack getItemCostPerStage(int index) { + if (itemsCost == null || index < 0 || index >= itemsCost.length) { + return null; + } + + return itemsCost[index]; + } + + @Override + public ItemStack[] getCurrentItemsProgress() { + ItemStack[] currentItemsProgress = new ItemStack[itemsCost.length]; + int index = 0; + for (ItemStack item : itemsCost) { + if (item == null) { + currentItemsProgress[index++] = null; + continue; + } + ItemStack copy = item.copy(); + copy.stackSize *= getCurrentStage(); + currentItemsProgress[index++] = copy; + } + + return currentItemsProgress; + } + + @Override + public ItemStack getCurrentItemProgress(int index) { + if (itemsCost == null || index < 0 || index >= itemsCost.length || itemsCost[index] == null) { + return null; + } + + ItemStack item = itemsCost[index].copy(); + item.stackSize *= getCurrentStage(); + return item; + } + + @Override + public ItemStack[] getTotalItemsCost() { + ItemStack[] totalItemsCost = new ItemStack[itemsCost.length]; + int index = 0; + for (ItemStack item : itemsCost) { + if (item == null) { + totalItemsCost[index++] = null; + continue; + } + ItemStack copy = item.copy(); + copy.stackSize *= getTotalStages(); + totalItemsCost[index++] = copy; + } + + return totalItemsCost; + } + + @Override + public ItemStack getTotalItemCost(int index) { + if (itemsCost == null || index < 0 || index >= itemsCost.length || itemsCost[index] == null) { + return null; + } + + ItemStack item = itemsCost[index].copy(); + item.stackSize *= getTotalStages(); + return item; + } + + @Override + public FluidStack[] getFluidsCostPerStage() { + return fluidsCost; + } + + @Override + public FluidStack getFluidCostPerStage(int index) { + if (fluidsCost == null || index < 0 || index >= fluidsCost.length || fluidsCost[index] == null) { + return null; + } + + return fluidsCost[index]; + } + + @Override + public FluidStack[] getCurrentFluidsProgress() { + if (fluidsCost == null) { + return null; + } + + FluidStack[] currentFluidsProgress = new FluidStack[fluidsCost.length]; + int index = 0; + for (FluidStack fluid : fluidsCost) { + if (fluid == null) { + currentFluidsProgress[index++] = null; + continue; + } + FluidStack copy = fluid.copy(); + copy.amount *= getCurrentStage(); + currentFluidsProgress[index++] = copy; + } + + return currentFluidsProgress; + } + + @Override + public FluidStack getCurrentFluidProgress(int index) { + if (fluidsCost == null || index < 0 || index >= fluidsCost.length || fluidsCost[index] == null) { + return null; + } + + FluidStack fluid = fluidsCost[index].copy(); + fluid.amount *= getCurrentStage(); + return fluid; + } + + @Override + public FluidStack[] getTotalFluidsCost() { + if (fluidsCost == null) { + return null; + } + + FluidStack[] totalFluidsCost = new FluidStack[fluidsCost.length]; + int index = 0; + for (FluidStack fluid : fluidsCost) { + if (fluid == null) { + totalFluidsCost[index++] = null; + continue; + } + FluidStack copy = fluid.copy(); + copy.amount *= getTotalStages(); + totalFluidsCost[index++] = copy; + } + + return totalFluidsCost; + } + + @Override + public FluidStack getTotalFluidCost(int index) { + if (fluidsCost == null || index < 0 || index >= fluidsCost.length) { + return null; + } + + FluidStack fluid = fluidsCost[index].copy(); + fluid.amount *= getTotalStages(); + return fluid; + } + + @Override + public ISP_Upgrade getUpgradeBeingBuilt() { + return currentUpgrade; + } + + @Override + public ISpaceBody getProjectLocation() { + return location; + } + + @Override + public UITexture getTexture() { + return texture; + } + + // #endregion + + // #region Setter/Builder + + public SpaceProject setProjectName(String spaceProjectName) { + name = spaceProjectName; + return this; + } + + public SpaceProject setProjectUnlocalizedName(String spaceProjectUnlocalizedName) { + unlocalizedName = spaceProjectUnlocalizedName; + return this; + } + + public SpaceProject setProjectVoltage(long spaceProjectVoltage) { + voltage = spaceProjectVoltage; + return this; + } + + public SpaceProject setProjectBuildTime(int spaceProjectBuildTime) { + buildTime = spaceProjectBuildTime; + return this; + } + + public SpaceProject setProjectStages(int spaceProjectTotalStages) { + totalStage = spaceProjectTotalStages; + return this; + } + + public SpaceProject setProjectItemsCost(ItemStack... spaceProjectItemsCost) { + itemsCost = spaceProjectItemsCost; + return this; + } + + public SpaceProject setProjectFluidsCost(FluidStack... spaceProjectFluidsCost) { + fluidsCost = spaceProjectFluidsCost; + return this; + } + + public SpaceProject setProjectUpgrades(ISP_Upgrade... spaceProjectUpgrades) { + for (ISP_Upgrade upgrade : spaceProjectUpgrades) { + upgrade.setUpgradeProject(this); + upgradesAvailable.put(upgrade.getUpgradeName(), upgrade); + } + return this; + } + + public SpaceProject setProjectTexture(UITexture projectTexture) { + texture = projectTexture; + return this; + } + + public SpaceProject setProjectRequirements(ISP_Requirements projectRequirements) { + requirements = projectRequirements; + return this; + } + + @Override + public void setCurrentUpgradeBeingBuilt(ISP_Upgrade newCurrentUpgrade) { + if (newCurrentUpgrade == null) { + return; + } + + if (totalStage == currentStage) { + currentUpgrade = newCurrentUpgrade.copy(); + currentUpgrade.setUpgradeProject(this); + } + } + + @Override + public void setProjectCurrentStage(int newCurrentStage) { + currentStage = newCurrentStage; + } + + @Override + public void setProjectLocation(ISpaceBody newLocation) { + location = newLocation; + } + + @Override + public void setBuiltUpgrade(ISP_Upgrade... upgrades) { + if (upgrades == null) { + return; + } + + for (ISP_Upgrade upgrade : upgrades) { + if (upgrade.equals(currentUpgrade)) { + currentUpgrade = null; + } + upgradesInstalled.put(upgrade.getUpgradeName(), upgrade); + } + } + + // #endregion + + // #region Other + + @Override + public ISpaceProject copy() { + SpaceProject copy = new SpaceProject().setProjectName(name) + .setProjectUnlocalizedName(unlocalizedName) + .setProjectVoltage(voltage) + .setProjectBuildTime(buildTime) + .setProjectItemsCost(itemsCost) + .setProjectFluidsCost(fluidsCost) + .setProjectStages(totalStage) + .setProjectTexture(texture) + .setProjectRequirements(requirements); + if (upgradesAvailable != null) { + ISP_Upgrade[] upgrades = new SP_Upgrade[upgradesAvailable.size()]; + int index = 0; + for (ISP_Upgrade upgrade : upgradesAvailable.values()) { + upgrades[index++] = upgrade.copy(); + } + copy.setProjectUpgrades(upgrades); + } + return copy; + } + + @Override + public void goToNextStage() { + currentStage++; + } + + @Override + public boolean meetsRequirements(UUID team) { + return meetsRequirements(team, true); + } + + @Override + public boolean meetsRequirements(UUID team, boolean checkLocation) { + if (requirements == null) { + return true; + } + + if (requirements.getBodyType() != null && checkLocation) { + if (!requirements.getBodyType() + .equals(location.getType())) { + return false; + } + } + + if (requirements.getStarType() != null && checkLocation) { + if (!requirements.getStarType() + .equals(location.getStarType())) { + return false; + } + } + + if (requirements.getProjects() != null) { + for (ISpaceProject project : requirements.getProjects()) { + if (!SpaceProjectManager.teamHasProject(team, project)) { + return false; + } + } + } + + return true; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof SpaceProject)) { + return false; + } + return getProjectName().equals(((SpaceProject) obj).getProjectName()); + } + + @Override + public boolean isFinished() { + return currentStage == totalStage; + } + + @Override + public boolean hasUpgrade(String upgradeName) { + return upgradesInstalled.containsKey(upgradeName); + } + + // #endregion +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/commands/SPM_Command.java b/src/main/java/gregtech/common/misc/spaceprojects/commands/SPM_Command.java new file mode 100644 index 0000000000..896c7e1052 --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/commands/SPM_Command.java @@ -0,0 +1,288 @@ +package gregtech.common.misc.spaceprojects.commands; + +import static gregtech.common.misc.spaceprojects.SpaceProjectManager.getLocation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +import net.minecraft.command.CommandBase; +import net.minecraft.command.ICommandSender; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.ChatComponentText; + +import gregtech.common.misc.spaceprojects.SpaceProjectManager; +import gregtech.common.misc.spaceprojects.SpaceProjectWorldSavedData; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceProject; + +/** + * @author BlueWeabo + */ +public class SPM_Command extends CommandBase { + + private static final String RESET = "reset"; + private static final String UNLOCK = "unlock"; + private static final String UNLOCK_UPGRADE = "unlock_upgrade"; + private static final String LOCK = "lock"; + private static final String LIST = "list"; + private static final String ALL = "-all"; + private static final String AVAILABLE = "-available"; + private static final String UNLOCKED = "-unlocked"; + private static final String COPY = "copy"; + + @Override + public String getCommandName() { + return "spm"; + } + + @Override + public int getRequiredPermissionLevel() { + return 0; + } + + @Override + public String getCommandUsage(ICommandSender sender) { + return "/" + this.getCommandName() + " <subCommand>. Available subCommands: reset, unlock, lock, list, copy"; + } + + @Override + public void processCommand(ICommandSender sender, String[] arguments) { + if (arguments.length < 1) { + printHelp(sender); + return; + } + switch (arguments[0]) { + case RESET: + if (!sender.canCommandSenderUseCommand(4, getCommandName())) { + sender.addChatMessage( + new ChatComponentText("You don't have the permissions to execute this command")); + return; + } + processReset(sender, arguments.length >= 2 ? arguments[1] : sender.getCommandSenderName()); + break; + case UNLOCK: + if (!sender.canCommandSenderUseCommand(4, getCommandName())) { + sender.addChatMessage( + new ChatComponentText("You don't have the permissions to execute this command")); + return; + } + if (arguments.length < 3) { + sender.addChatMessage( + new ChatComponentText("Not enough arguments. Needs to mention a project and a location")); + return; + } + processUnlock( + sender, + arguments[1], + arguments[2], + arguments.length >= 4 ? arguments[3] : sender.getCommandSenderName()); + break; + case UNLOCK_UPGRADE: + if (!sender.canCommandSenderUseCommand(4, getCommandName())) { + sender.addChatMessage( + new ChatComponentText("You don't have the permissions to execute this command")); + return; + } + if (arguments.length < 4) { + sender.addChatMessage( + new ChatComponentText( + "Not enough arguments. Needs to mention a project a location and an upgrade name")); + return; + } + processUnlock( + sender, + arguments[1], + arguments[2], + arguments[3], + arguments.length >= 5 ? arguments[4] : sender.getCommandSenderName()); + break; + case LOCK: + if (!sender.canCommandSenderUseCommand(4, getCommandName())) { + sender.addChatMessage( + new ChatComponentText("You don't have the permissions to execute this command")); + return; + } + if (arguments.length < 3) { + sender.addChatMessage( + new ChatComponentText("Not enough arguments. Needs to mention a project and a location")); + return; + } + processLock( + sender, + arguments[1], + arguments[2], + arguments.length >= 4 ? arguments[3] : sender.getCommandSenderName()); + case LIST: + if (arguments.length < 2) { + sender.addChatMessage( + new ChatComponentText( + "No Argument for list subCommand. Usage /spm list -all, -available or -unlocked")); + return; + } + processList(sender, arguments[1], arguments.length >= 3 ? arguments[2] : sender.getCommandSenderName()); + break; + case COPY: + if (!sender.canCommandSenderUseCommand(4, getCommandName())) { + sender.addChatMessage( + new ChatComponentText("You don't have the permissions to execute this command")); + return; + } + if (arguments.length < 3) { + sender.addChatMessage(new ChatComponentText("Not enough arguments. Needs to mention 2 players")); + return; + } + processCopy(sender, arguments[1], arguments[2]); + break; + } + } + + @Override + public List<String> addTabCompletionOptions(ICommandSender sender, String[] arguments) { + List<String> autoComplete = new ArrayList<>(); + String filter = arguments.length == 0 ? "" : arguments[0].trim(); + switch (arguments.length) { + case 1 -> autoComplete.addAll(Arrays.asList(getSubCommands())); + case 2 -> { + filter = arguments[1].trim(); + if (arguments[0].equals(LIST)) { + autoComplete.addAll(Arrays.asList(getListArguments())); + } else if (arguments[0].equals(COPY) || arguments[0].equals(RESET)) { + autoComplete.addAll(Arrays.asList(getPlayers())); + } else { + autoComplete.addAll(Arrays.asList(getProjects())); + } + } + case 3 -> { + filter = arguments[2].trim(); + if (arguments[1].equals(ALL)) {} else if (arguments[0].equals(LIST)) { + autoComplete.addAll(Arrays.asList(getPlayers())); + } else { + autoComplete.addAll(Arrays.asList(getLocations())); + } + } + case 4 -> { + filter = arguments[3].trim(); + if (arguments[0].equals(UNLOCK_UPGRADE)) { + ISpaceProject project = SpaceProjectManager.getProject(arguments[2]); + if (project != null) { + autoComplete.addAll( + project.getAllUpgrades() + .stream() + .map(ISpaceProject.ISP_Upgrade::getUnlocalizedName) + .collect(Collectors.toList())); + } + } else { + autoComplete.addAll(Arrays.asList(getPlayers())); + } + } + } + String finalFilter = filter; + return autoComplete.stream() + .filter(s -> finalFilter.isEmpty() || s.startsWith(finalFilter)) + .collect(Collectors.toList()); + } + + private String[] getPlayers() { + return MinecraftServer.getServer() + .getAllUsernames(); + } + + private String[] getLocations() { + return SpaceProjectManager.getLocationNames() + .toArray(new String[0]); + } + + private String[] getProjects() { + return SpaceProjectManager.getProjectsMap() + .keySet() + .toArray(new String[0]); + } + + private String[] getSubCommands() { + return new String[] { RESET, COPY, UNLOCK, UNLOCK_UPGRADE, LOCK, LIST }; + } + + private String[] getListArguments() { + return new String[] { ALL, AVAILABLE, UNLOCKED }; + } + + private void processReset(ICommandSender sender, String playerName) { + UUID tID = SpaceProjectManager.getPlayerUUIDFromName(playerName); + SpaceProjectManager.spaceTeamProjects.put(tID, null); + SpaceProjectWorldSavedData.INSTANCE.markDirty(); + sender.addChatMessage(new ChatComponentText("Cleared away map")); + } + + private void processLock(ICommandSender sender, String projectName, String location, String playerName) { + UUID tID = SpaceProjectManager.getPlayerUUIDFromName(playerName); + SpaceProjectManager.addTeamProject(tID, getLocation(location), projectName, null); + sender.addChatMessage(new ChatComponentText("Project locked")); + } + + private void processUnlock(ICommandSender sender, String projectName, String location, String playerName) { + UUID tID = SpaceProjectManager.getPlayerUUIDFromName(playerName); + ISpaceProject tProject = SpaceProjectManager.getTeamProjectOrCopy(tID, projectName, getLocation(location)); + if (tProject != null) { + tProject.setProjectCurrentStage(tProject.getTotalStages()); + SpaceProjectManager.addTeamProject(tID, getLocation(location), projectName, tProject); + sender.addChatMessage(new ChatComponentText("Project unlocked")); + } else { + sender.addChatMessage(new ChatComponentText("Incorrect internal project name. Try again")); + } + } + + private void processUnlock(ICommandSender sender, String projectName, String location, String upgradeName, + String playerName) { + UUID tID = SpaceProjectManager.getPlayerUUIDFromName(playerName); + ISpaceProject tProject = SpaceProjectManager.getTeamProjectOrCopy(tID, projectName, getLocation(location)); + if (tProject != null) { + ISpaceProject.ISP_Upgrade upgrade = tProject.getUpgrade(upgradeName); + if (upgrade == null) { + sender.addChatMessage(new ChatComponentText("Incorrect internal project upgrade name. Try again")); + return; + } + if (!tProject.isFinished()) { + tProject.setProjectCurrentStage(tProject.getTotalStages()); + SpaceProjectManager.addTeamProject(tID, getLocation(location), projectName, tProject); + } + tProject.setBuiltUpgrade(upgrade); + sender.addChatMessage(new ChatComponentText("Project Upgrade unlocked")); + } else { + sender.addChatMessage(new ChatComponentText("Incorrect internal project name. Try again")); + } + } + + private void processList(ICommandSender sender, String argument, String playerName) { + UUID tID = SpaceProjectManager.getPlayerUUIDFromName(playerName); + switch (argument) { + case ALL -> { + for (String project : SpaceProjectManager.getProjectsMap() + .keySet()) { + sender.addChatMessage(new ChatComponentText(project)); + } + } + case AVAILABLE -> { + for (ISpaceProject project : SpaceProjectManager.getAllProjects()) { + if (project.meetsRequirements(tID, false)) { + sender.addChatMessage(new ChatComponentText(project.getProjectName())); + } + } + } + case UNLOCKED -> { + for (ISpaceProject project : SpaceProjectManager.getTeamSpaceProjects(tID)) { + sender.addChatMessage(new ChatComponentText(project.getProjectName())); + } + } + } + } + + private void processCopy(ICommandSender sender, String playerToCopyFrom, String playerCopyingTo) { + // This will take a while + } + + private void printHelp(ICommandSender sender) { + + } +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/commands/SP_Command.java b/src/main/java/gregtech/common/misc/spaceprojects/commands/SP_Command.java new file mode 100644 index 0000000000..3c4ad00932 --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/commands/SP_Command.java @@ -0,0 +1,166 @@ +package gregtech.common.misc.spaceprojects.commands; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.stream.Collectors; + +import net.minecraft.command.CommandBase; +import net.minecraft.command.ICommandSender; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.server.MinecraftServer; +import net.minecraft.util.EnumChatFormatting; + +import org.apache.commons.lang3.tuple.Pair; + +import gregtech.api.util.GT_Utility; +import gregtech.common.misc.spaceprojects.SpaceProjectManager; + +/** + * @author BlueWeabo + */ +public class SP_Command extends CommandBase { + + private static final Set<Pair<EntityPlayerMP, EntityPlayerMP>> invite = Collections + .newSetFromMap(new WeakHashMap<>()); + private static final Set<EntityPlayerMP> confirm = Collections.newSetFromMap(new WeakHashMap<>()); + + private static final String INVITE = "invite"; + private static final String ACCEPT = "accept"; + private static final String LEAVE = "leave"; + private static final String CONFIRM = "confirm"; + + @Override + public String getCommandName() { + return "sp"; + } + + @Override + public int getRequiredPermissionLevel() { + return 0; + } + + @Override + public String getCommandUsage(ICommandSender sender) { + return "/" + getCommandName() + "<subCommand> [PlayerName]"; + } + + @Override + public void processCommand(ICommandSender sender, String[] arguments) { + if (arguments.length < 1) { + return; + } + switch (arguments[0]) { + case INVITE -> { + if (arguments.length < 2) { + return; + } + processInvite(sender, arguments[1]); + } + case ACCEPT -> { + if (arguments.length < 2) { + return; + } + processAccept(sender, arguments[1]); + } + case LEAVE -> processLeave(sender); + case CONFIRM -> processConfirm(sender); + } + } + + private void processInvite(ICommandSender sender, String playerInvited) { + EntityPlayerMP teamLeader = getCommandSenderAsPlayer(sender); + EntityPlayerMP teamMember = getPlayer(sender, playerInvited); + invite.add(Pair.of(teamMember, teamLeader)); + String message = EnumChatFormatting.GOLD + teamLeader.getCommandSenderName() + + EnumChatFormatting.RESET + + " has sent you an invite to join their team. Accept it with" + + EnumChatFormatting.GOLD + + " /sp accept " + + teamLeader.getCommandSenderName(); + GT_Utility.sendChatToPlayer(teamMember, message); + } + + private void processAccept(ICommandSender sender, String playerInviter) { + EntityPlayerMP teamMember = getCommandSenderAsPlayer(sender); + EntityPlayerMP teamLeader = getPlayer(sender, playerInviter); + if (invite.contains(Pair.of(teamMember, teamLeader))) { + String message = EnumChatFormatting.GOLD + teamMember.getCommandSenderName() + + EnumChatFormatting.RESET + + " has accepted the invite."; + SpaceProjectManager.putInTeam(teamMember.getUniqueID(), teamLeader.getUniqueID()); + GT_Utility.sendChatToPlayer(teamLeader, message); + invite.remove(Pair.of(teamMember, teamLeader)); + } + } + + private void processLeave(ICommandSender sender) { + EntityPlayerMP player = getCommandSenderAsPlayer(sender); + String message = "Are you sure you want to leave the team. You will lose all progress. Use " + + EnumChatFormatting.GOLD + + "/sp confirm" + + EnumChatFormatting.RESET + + " to confirm this. This does nothing if you are the team leader."; + GT_Utility.sendChatToPlayer(player, message); + confirm.add(player); + } + + private void processConfirm(ICommandSender sender) { + EntityPlayerMP player = getCommandSenderAsPlayer(sender); + if (confirm.contains(player)) { + String message = "Successfully left the team."; + SpaceProjectManager.putInTeam(player.getUniqueID(), player.getUniqueID()); + GT_Utility.sendChatToPlayer(player, message); + confirm.remove(player); + } + } + + @Override + public List<String> addTabCompletionOptions(ICommandSender sender, String[] arguments) { + List<String> autoComplete = new ArrayList<>(); + String filter = arguments.length == 0 ? "" : arguments[0].trim(); + switch (arguments.length) { + case 1 -> autoComplete.addAll(Arrays.asList(getSubCommands())); + case 2 -> { + filter = arguments[1].trim(); + if (arguments[0].equals(INVITE)) { + autoComplete.addAll(Arrays.asList(getPlayers())); + break; + } + + if (arguments[0].equals(CONFIRM)) { + Optional<Pair<EntityPlayerMP, EntityPlayerMP>> pairOpt = invite.stream() + .filter( + (e) -> e.getKey() + .getUniqueID() == getCommandSenderAsPlayer(sender).getUniqueID()) + .findFirst(); + if (pairOpt.isPresent()) { + autoComplete.add( + SpaceProjectManager.getPlayerNameFromUUID( + pairOpt.get() + .getRight() + .getUniqueID())); + } + } + } + } + String finalFilter = filter; + return autoComplete.stream() + .filter(s -> finalFilter.isEmpty() || s.startsWith(finalFilter)) + .collect(Collectors.toList()); + } + + private String[] getPlayers() { + return MinecraftServer.getServer() + .getAllUsernames(); + } + + private String[] getSubCommands() { + return new String[] { INVITE, ACCEPT, LEAVE, CONFIRM }; + } + +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/enums/JsonVariables.java b/src/main/java/gregtech/common/misc/spaceprojects/enums/JsonVariables.java new file mode 100644 index 0000000000..665f15af74 --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/enums/JsonVariables.java @@ -0,0 +1,22 @@ +package gregtech.common.misc.spaceprojects.enums; + +public class JsonVariables { + + public static final String PAIR_LEFT = "pairLeft"; + public static final String PAIR_RIGHT = "pairRight"; + + public static final String MAP_UUID = "mapUUID"; + public static final String MAP_MAP = "mapMap"; + public static final String MAP_PAIR = "mapPair"; + public static final String MAP_PROJECT = "mapProject"; + + public static final String PROJECT_NAME = "projectName"; + public static final String PROJECT_CURRENT_STAGE = "projectCurrentStage"; + public static final String PROJECT_UPGRADES_BUILT = "projectUpgradesBuilt"; + public static final String PROJECT_LOCATION = "projectLocation"; + public static final String PROJECT_CURRENT_UPGRADE = "projectCurrentUpgrade"; + + public static final String UPGRADE_NAME = "upgradeName"; + public static final String UPGRADE_CURRENT_STAGE = "upgradeCurrentStage"; + public static final String UPGRADE_PROJECT_PARENT = "upgradeProjectParent"; +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/enums/SolarSystem.java b/src/main/java/gregtech/common/misc/spaceprojects/enums/SolarSystem.java new file mode 100644 index 0000000000..70dee3269d --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/enums/SolarSystem.java @@ -0,0 +1,104 @@ +package gregtech.common.misc.spaceprojects.enums; + +import static gregtech.api.enums.Mods.GregTech; +import static gregtech.common.misc.spaceprojects.enums.SpaceBodyType.AsteroidBelt; +import static gregtech.common.misc.spaceprojects.enums.SpaceBodyType.DwarfPlanet; +import static gregtech.common.misc.spaceprojects.enums.SpaceBodyType.GasGiant; +import static gregtech.common.misc.spaceprojects.enums.SpaceBodyType.IceGiant; +import static gregtech.common.misc.spaceprojects.enums.SpaceBodyType.NaturalSatellite; +import static gregtech.common.misc.spaceprojects.enums.SpaceBodyType.Planet; +import static gregtech.common.misc.spaceprojects.enums.SpaceBodyType.Star; +import static gregtech.common.misc.spaceprojects.enums.StarType.GClass; +import static gregtech.common.misc.spaceprojects.enums.StarType.NotAStar; + +import com.gtnewhorizons.modularui.api.drawable.UITexture; + +import gregtech.common.misc.spaceprojects.SpaceProjectManager; +import gregtech.common.misc.spaceprojects.interfaces.ISpaceBody; + +/** + * An enum of all space bodies in the Sol Solar System. Or to be exact the more important ones + * + * @author BlueWeabo + */ +public enum SolarSystem implements ISpaceBody { + + Sol(Star, GClass), + Overworld(Planet), + Moon(NaturalSatellite), + Mars(Planet), + Deimos(NaturalSatellite), + Phobos(NaturalSatellite), + Mercury(Planet), + Venus(Planet), + Jupiter(GasGiant), + Io(NaturalSatellite), + Ganymede(NaturalSatellite), + Europa(NaturalSatellite), + Callisto(NaturalSatellite), + Saturn(GasGiant), + Mimas(NaturalSatellite), + Enceladus(NaturalSatellite), + Tethys(NaturalSatellite), + Rhea(NaturalSatellite), + Titan(NaturalSatellite), + Hyperion(NaturalSatellite), + Iapetus(NaturalSatellite), + Phoebe(NaturalSatellite), + Uranus(IceGiant), + Miranda(NaturalSatellite), + Ariel(NaturalSatellite), + Umbriel(NaturalSatellite), + Titania(NaturalSatellite), + Oberon(NaturalSatellite), + Neptune(IceGiant), + Proteus(NaturalSatellite), + Triton(NaturalSatellite), + Nereid(NaturalSatellite), + Ceres(DwarfPlanet), + Pluto(DwarfPlanet), + Arrokoth(DwarfPlanet), + MakeMake(DwarfPlanet), + KuiperBelt(AsteroidBelt), + NONE(SpaceBodyType.NONE); + + private final SpaceBodyType spaceBody; + private final StarType star; + private final UITexture texture; + + SolarSystem(SpaceBodyType aType) { + this(aType, NotAStar); + } + + SolarSystem(SpaceBodyType aType, StarType aStarType) { + star = aStarType; + spaceBody = aType; + texture = UITexture.fullImage(GregTech.ID, "solarsystem/" + getName()); + SpaceProjectManager.addLocation(this); + } + + @Override + public StarType getStarType() { + return star; + } + + @Override + public SpaceBodyType getType() { + return spaceBody; + } + + @Override + public String getName() { + return name(); + } + + @Override + public UITexture getTexture() { + return texture; + } + + @Override + public String getUnlocalizedName() { + return "gt.solar.system." + getName().toLowerCase(); + } +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/enums/SpaceBodyType.java b/src/main/java/gregtech/common/misc/spaceprojects/enums/SpaceBodyType.java new file mode 100644 index 0000000000..8ca8b663da --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/enums/SpaceBodyType.java @@ -0,0 +1,17 @@ +package gregtech.common.misc.spaceprojects.enums; + +/** + * @author BlueWeabo + */ +public enum SpaceBodyType { + Star, + NeutronStar, + Planet, + NaturalSatellite, + AsteroidBelt, + BlackHole, + GasGiant, + DwarfPlanet, + IceGiant, + NONE +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/enums/StarType.java b/src/main/java/gregtech/common/misc/spaceprojects/enums/StarType.java new file mode 100644 index 0000000000..7585c95aa0 --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/enums/StarType.java @@ -0,0 +1,32 @@ +package gregtech.common.misc.spaceprojects.enums; + +/** + * @author BlueWeabo + */ +public enum StarType { + + OClass(30000, 100), + BClass(10000, 20), + AClass(20, 5), + FClass(4, 2), + GClass(1, 1), + KClass(0.4, 0.5f), + MClass(0.08, 0.1f), + NotAStar(0, 0); + + private final double solarLuminosity; + private final float costMultiplier; + + StarType(double solarLuminosity, float costMultiplier) { + this.solarLuminosity = solarLuminosity; + this.costMultiplier = costMultiplier; + } + + public double getSolarLuminosity() { + return solarLuminosity; + } + + public float getCostMultiplier() { + return costMultiplier; + } +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/enums/UpgradeStatus.java b/src/main/java/gregtech/common/misc/spaceprojects/enums/UpgradeStatus.java new file mode 100644 index 0000000000..3e662720ac --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/enums/UpgradeStatus.java @@ -0,0 +1,11 @@ +package gregtech.common.misc.spaceprojects.enums; + +/** + * @author BlueWeabo + */ +public enum UpgradeStatus { + Locked, + Unlocked, + InProgress, + Finished +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/interfaces/ISpaceBody.java b/src/main/java/gregtech/common/misc/spaceprojects/interfaces/ISpaceBody.java new file mode 100644 index 0000000000..79eba4c968 --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/interfaces/ISpaceBody.java @@ -0,0 +1,37 @@ +package gregtech.common.misc.spaceprojects.interfaces; + +import com.gtnewhorizons.modularui.api.drawable.UITexture; + +import gregtech.common.misc.spaceprojects.enums.SpaceBodyType; +import gregtech.common.misc.spaceprojects.enums.StarType; + +/** + * @author BlueWeabo + */ +public interface ISpaceBody { + + /** + * @return The star type of the space body, if its a star + */ + StarType getStarType(); + + /** + * @return The type of space body it is + */ + SpaceBodyType getType(); + + /** + * @return The internal name of the space body + */ + String getName(); + + /** + * @return The texture of the space body used for UI + */ + UITexture getTexture(); + + /** + * @return The Unlocalized name for this body + */ + String getUnlocalizedName(); +} diff --git a/src/main/java/gregtech/common/misc/spaceprojects/interfaces/ISpaceProject.java b/src/main/java/gregtech/common/misc/spaceprojects/interfaces/ISpaceProject.java new file mode 100644 index 0000000000..51ae03ff30 --- /dev/null +++ b/src/main/java/gregtech/common/misc/spaceprojects/interfaces/ISpaceProject.java @@ -0,0 +1,430 @@ +package gregtech.common.misc.spaceprojects.interfaces; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; + +import com.gtnewhorizons.modularui.api.drawable.UITexture; + +import gregtech.common.misc.spaceprojects.enums.SpaceBodyType; +import gregtech.common.misc.spaceprojects.enums.StarType; +import gregtech.common.misc.spaceprojects.enums.UpgradeStatus; + +/** + * @author BlueWeabo + */ +public interface ISpaceProject { + + /** + * @return the internal name of the project. + */ + String getProjectName(); + + /** + * @return Unlocalized name of the project. + */ + String getUnlocalizedName(); + + /** + * @return Localized name of the project using StatCollect#translateToLocal. + */ + String getLocalizedName(); + + /** + * @return The voltage the project requires to be built at. Used by the project manager. + */ + long getProjectVoltage(); + + /** + * @return The duration it takes to build out one(1) stage of the project. The time returned is in ticks + */ + int getProjectBuildTime(); + + /** + * @return The Current Progress of the project in percentage form. 1 being 100% and 0 being 0%. + */ + float getProjectCurrentProgress(); + + /** + * @return Currently unused, but this is the project's tier. Will be used to determine the min-tier motors needed on + * the Space Elevator + */ + int getProjectTier(); + + /** + * @return The Current stage of the project + */ + int getCurrentStage(); + + /** + * @return The Total amount of stages the project has + */ + int getTotalStages(); + + /** + * @return a Collection of all upgrades the project has + */ + Collection<ISP_Upgrade> getAllUpgrades(); + + /** + * @return a Map of all upgrades that have been built. + */ + Map<String, ISP_Upgrade> getUpgradesBuiltMap(); + + /** + * @return all built upgrades + */ + Collection<ISP_Upgrade> getAllBuiltUpgrades(); + + /** + * @param upgradeName The name of the upgrade wanted + * @return The upgrade with the appropriate name found in the available upgrades for the project + */ + ISP_Upgrade getUpgrade(String upgradeName); + + /** + * @return The Items cost required per stage in an array form. Used for making the recipe. + */ + ItemStack[] getItemsCostPerStage(); + + /** + * @param index Index at which the itemstack is found at + * @return an item's cost at the appropriate index. Null otherwise if it goes above the index or there are no item + * costs + */ + ItemStack getItemCostPerStage(int index); + + /** + * @return The Items current progress in an array form. + */ + ItemStack[] getCurrentItemsProgress(); + + /** + * @param index Index at which the itemstack is found at + * @return an item's current progress at the appropriate index. Null otherwise if it goes above the index or there + * are no item costs + */ + ItemStack getCurrentItemProgress(int index); + + /** + * @return The items total cost required in an array form. + */ + ItemStack[] getTotalItemsCost(); + + /** + * @param index Index at which the itemstack is found at + * @return an item's total cost at the appropriate index. Null otherwise if it goes above the index or there are no + * item costs + */ + ItemStack getTotalItemCost(int index); + + /** + * @return The fluids cost required per stage in an array form. Used for making the recipe. + */ + FluidStack[] getFluidsCostPerStage(); + + /** + * @param index Index at which the fluidstack is found at + * @return a fluid's cost at the appropriate index. Null otherwise if it goes above the index or there are no fluid + * costs + */ + FluidStack getFluidCostPerStage(int index); + + /** + * @return The fluids current progress in an array form. Null if there are no fluid costs + */ + FluidStack[] getCurrentFluidsProgress(); + + /** + * @param index Index at which the fluidstack is found at + * @return a fluid's current progress at the appropriate index. Null otherwise if it goes above the index or there + * are no fluid costs + */ + FluidStack getCurrentFluidProgress(int index); + + /** + * @return The fluids total cost required in an array form. Null if there are no fluid costs + */ + FluidStack[] getTotalFluidsCost(); + + /** + * @param index Index at which the fluidstack is found at + * @return a fluid's total cost at the appropriate index. Null otherwise if it goes above the index or there are no + * fluid costs + */ + FluidStack getTotalFluidCost(int index); + + /** + * @return The current upgrade for this project, which is being built + */ + ISP_Upgrade getUpgradeBeingBuilt(); + + /** + * @return The location of the project + */ + ISpaceBody getProjectLocation(); + + /** + * @return The texture used in GUIs for the project + */ + UITexture getTexture(); + + /** + * Sets the current stage of the project + */ + void setProjectCurrentStage(int stage); + + /** + * Sets the current upgrade, which needs to be built + */ + void setCurrentUpgradeBeingBuilt(ISP_Upgrade upgrade); + + /** + * Sets the project's location when it starts being built + */ + void setProjectLocation(ISpaceBody newLocation); + + /** + * Sets the project's upgrades, which have been built + */ + void setBuiltUpgrade(ISP_Upgrade... upgrades); + + /** + * Goes to the next stage of the project + */ + void goToNextStage(); + + /** + * Creates a copy of the space project + */ + ISpaceProject copy(); + + /** + * Checks if the project meets all requirements with its current location + * + * @param team Team wanting said project and checking their projects + * @return true if all requirements met, false otherwise + */ + boolean meetsRequirements(UUID team); + + /** + * Checks if the project meets requirements if it requires other projects, unless {@code checkLocation} is true, + * then it also checks for the location + * + * @param team Team wanting said project and checking their projects + * @param checkLocation If the location position should be checked + * @return true if all requirements met, false otherwise + */ + boolean meetsRequirements(UUID team, boolean checkLocation); + + /** + * Checks if the projects is finished + */ + boolean isFinished(); + + /** + * Checks if the project has a certain upgrade installed or not + * + * @param upgradeName Upgrade being searched for + * @return True if that upgrade has been installed, false otherwise + */ + boolean hasUpgrade(String upgradeName); + + /** + * @author BlueWeabo + */ + interface ISP_Upgrade { + + /** + * @return internal name of the upgrade + */ + String getUpgradeName(); + + /** + * @return unlocalized name of the upgrade + */ + String getUnlocalizedName(); + + /** + * @return localized name of the upgrade + */ + String getLocalizedName(); + + /** + * @return The Items cost required per stage in an array form. Used for making the recipe. + */ + ItemStack[] getItemsCostPerStage(); + + /** + * @param index Index at which the itemstack is found at + * @return an item's cost at the appropriate index. Null otherwise if it goes above the index or there are no + * item costs + */ + ItemStack getItemCostPerStage(int index); + + /** + * @return The Items current progress in an array form. + */ + ItemStack[] getCurrentItemsProgress(); + + /** + * @param index Index at which the itemstack is found at + * @return an item's current progress at the appropriate index. Null otherwise if it goes above the index or + * there are no item costs + */ + ItemStack getCurrentItemProgress(int index); + + /** + * @return The items total cost required in an array form. + */ + ItemStack[] getTotalItemsCost(); + + /** + * @param index Index at which the itemstack is found at + * @return an item's total cost at the appropriate index. Null otherwise if it goes above the index or there are + * no item costs + */ + ItemStack getTotalItemCost(int index); + + /** + * @return The fluids cost required per stage in an array form. Used for making the recipe. + */ + FluidStack[] getFluidsCostPerStage(); + + /** + * @param index Index at which the fluidstack is found at + * @return a fluid's cost at the appropriate index. Null otherwise if it goes above the index or there are no + * fluid costs + */ + FluidStack getFluidCostPerStage(int index); + + /** + * @return The fluids current progress in an array form. Null if there are no fluid costs + */ + FluidStack[] getCurrentFluidsProgress(); + + /** + * @param index Index at which the fluidstack is found at + * @return a fluid's current progress at the appropriate index. Null otherwise if it goes above the index or + * there are no fluid costs + */ + FluidStack getCurrentFluidProgress(int index); + + /** + * @return The fluids total cost required in an array form. Null if there are no fluid costs + */ + FluidStack[] getTotalFluidsCost(); + + /** + * @param index Index at which the fluidstack is found at + * @return a fluid's total cost at the appropriate index. Null otherwise if it goes above the index or there are + * no fluid costs + */ + FluidStack getTotalFluidCost(int index); + + /** + * @return the total stages an upgrade has + */ + int getTotalStages(); + + /** + * @return the build time for the upgrade to go to its next stage + */ + int getUpgradeBuildTime(); + + /** + * @return current stage of the upgrade + */ + int getCurrentStage(); + + /** + * @return The Current Progress of the upgrade in percentage form. 1 being 100% and 0 being 0%. + */ + float getCurrentProgress(); + + /** + * @return The voltage at which the upgrade requires to be build at. + */ + long getVoltage(); + + /** + * Unused, unsure if it will get a sure + */ + UpgradeStatus getStatus(); + + /** + * @return the requirements the upgrade has + */ + ISP_Requirements getUpgradeRequirements(); + + /** + * @return the parent project, which the upgrade belongs to + */ + ISpaceProject getParentProject(); + + /** + * @param project The project the upgrade belongs to + */ + void setUpgradeProject(ISpaceProject project); + + /** + * Sets the current stage of the upgrade + * + * @param stage the stage to set + */ + void setUpgradeCurrentStage(int stage); + + /** + * Checks if the team has met all requirements to be able to build said upgrade + * + * @param team The one starting the upgrade + * @return true if all requirements are met, false otherwise + */ + boolean meetsRequirements(UUID team); + + /** + * Creates a copy of the upgrade + */ + ISP_Upgrade copy(); + + /** + * Goes to the next stage of the upgrade + */ + void goToNextStage(); + + /** + * @return true if the upgrade has finished all of its stages, false otherwise + */ + boolean isFinished(); + } + + /** + * @author BlueWeabo + */ + interface ISP_Requirements { + + /** + * @return Space Body Type required by the project/upgrade + */ + SpaceBodyType getBodyType(); + + /** + * @return Star Type required by the project/upgrade + */ + StarType getStarType(); + + /** + * @return a list of all project required for the team to have to unlock it + */ + List<ISpaceProject> getProjects(); + + /** + * @return a list of all upgrades an upgrade can have as required. + */ + List<ISP_Upgrade> getUpgrades(); + } +} |