diff --git a/assets/Data/entity/items/debug_tools.json b/assets/Data/entity/items/debug_tools.json index 6937b5b3..4c6b6061 100644 --- a/assets/Data/entity/items/debug_tools.json +++ b/assets/Data/entity/items/debug_tools.json @@ -142,6 +142,7 @@ "path" : "Models/basic/geometry/unitvector.glb" } }, + "clientSidePrimary" : "PLACE_FAB", "clientSideSecondary": "SELECT_FAB", "collidable": { "type" : "CUBE", diff --git a/assets/Scripts/client/clienthooks.ts b/assets/Scripts/client/clienthooks.ts index 84991042..411572e7 100644 --- a/assets/Scripts/client/clienthooks.ts +++ b/assets/Scripts/client/clienthooks.ts @@ -52,5 +52,11 @@ export const clientHooks: Hook[] = [ callback: (engine: Engine) => { engine.classes.menuUtils.static.openFabSelection() } + }, + { + signal: "PLACE_FAB", + callback: (engine: Engine) => { + engine.classes.voxelUtils.static.placeFab() + } } ] \ No newline at end of file diff --git a/assets/Scripts/types/host/client/client-voxel-utils.ts b/assets/Scripts/types/host/client/client-voxel-utils.ts index 50a06df2..15dc26b3 100644 --- a/assets/Scripts/types/host/client/client-voxel-utils.ts +++ b/assets/Scripts/types/host/client/client-voxel-utils.ts @@ -18,4 +18,9 @@ export interface ClientVoxelUtils { */ readonly dig: () => void + /** + * Places the currently selected fab + */ + readonly placeFab: () => void + } \ No newline at end of file diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 763eb261..c3eaccb0 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1558,6 +1558,7 @@ New block type Fix inventory item tooltip not clearing More item icons Toolbar preview ui element +Fab tool can place fabs diff --git a/src/main/java/electrosphere/client/script/ScriptClientVoxelUtils.java b/src/main/java/electrosphere/client/script/ScriptClientVoxelUtils.java index 22259a9d..522a1e97 100644 --- a/src/main/java/electrosphere/client/script/ScriptClientVoxelUtils.java +++ b/src/main/java/electrosphere/client/script/ScriptClientVoxelUtils.java @@ -9,9 +9,11 @@ import electrosphere.audio.movement.MovementAudioService.InteractionType; import electrosphere.client.entity.camera.CameraEntityUtils; import electrosphere.client.terrain.editing.TerrainEditing; import electrosphere.collision.CollisionEngine; +import electrosphere.controls.cursor.CursorState; import electrosphere.engine.Globals; import electrosphere.engine.assetmanager.AssetDataStrings; import electrosphere.entity.Entity; +import electrosphere.entity.EntityUtils; import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.server.physics.terrain.manager.ServerTerrainChunk; @@ -134,4 +136,22 @@ public class ScriptClientVoxelUtils { )); } + /** + * Places the currently selected fab + */ + @Export + public static void placeFab(){ + if(Globals.cursorState.getSelectedFabPath() != null){ + String fabPath = Globals.cursorState.getSelectedFabPath(); + Vector3d fabCursorPos = EntityUtils.getPosition(CursorState.getFabCursor()); + Vector3i chunkPos = Globals.clientWorldData.convertRealToWorldSpace(fabCursorPos); + Vector3i voxelPos = Globals.clientWorldData.convertRealToBlockSpace(fabCursorPos); + Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestPlaceFabMessage( + chunkPos.x, chunkPos.y, chunkPos.z, + voxelPos.x, voxelPos.y, voxelPos.z, + fabPath + )); + } + } + } diff --git a/src/main/java/electrosphere/client/ui/menu/ingame/FabMenus.java b/src/main/java/electrosphere/client/ui/menu/ingame/FabMenus.java index c909de05..667a649d 100644 --- a/src/main/java/electrosphere/client/ui/menu/ingame/FabMenus.java +++ b/src/main/java/electrosphere/client/ui/menu/ingame/FabMenus.java @@ -57,8 +57,8 @@ public class FabMenus { //attach scrollable after search input for organzation purposes fabSelectionPanelWindow.addChild(FabSelectionPanel.createFabSelectionPanel((File selectedFile) -> { BlockFab fab = BlockFab.read(selectedFile); - LoggerInterface.loggerEngine.WARNING("" + fab.getDimensions()); Globals.cursorState.setSelectedFab(fab); + Globals.cursorState.setSelectedFabPath("./assets/Data/fab/" + selectedFile.getName()); })); Globals.signalSystem.post(SignalType.YOGA_APPLY,fabSelectionPanelWindow); diff --git a/src/main/java/electrosphere/controls/cursor/CursorState.java b/src/main/java/electrosphere/controls/cursor/CursorState.java index 4b4cb101..8df6bcc6 100644 --- a/src/main/java/electrosphere/controls/cursor/CursorState.java +++ b/src/main/java/electrosphere/controls/cursor/CursorState.java @@ -86,6 +86,16 @@ public class CursorState { */ static Entity playerFabCursor; + /** + * The currently selected fab + */ + private BlockFab selectedFab = null; + + /** + * The path for the selected fab + */ + private String selectedFabPath = null; + /** * Creates the cursor entities */ @@ -318,6 +328,30 @@ public class CursorState { CursorState.makeFabVisible(); } + /** + * Gets the currently selected fab + * @return The currently selected fab + */ + public BlockFab getSelectedFab(){ + return this.selectedFab; + } + + /** + * Sets the selected fab path + * @param path The path + */ + public void setSelectedFabPath(String path){ + this.selectedFabPath = path; + } + + /** + * Gets the selected fab's path + * @return The path + */ + public String getSelectedFabPath(){ + return this.selectedFabPath; + } + /** * Gets the size of the block cursor * @return The size of the block cursor @@ -381,5 +415,13 @@ public class CursorState { } } } + + /** + * Gets the fab cursor + * @return The fab cursor + */ + public static Entity getFabCursor(){ + return playerFabCursor; + } } diff --git a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java index 4a263fed..ab674768 100644 --- a/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/NetworkMessage.java @@ -250,6 +250,11 @@ public abstract class NetworkMessage { rVal = TerrainMessage.parseRequestEditBlockMessage(byteBuffer,pool); } break; + case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTPLACEFAB: + if(TerrainMessage.canParseMessage(byteBuffer,secondByte)){ + rVal = TerrainMessage.parseRequestPlaceFabMessage(byteBuffer,pool); + } + break; } break; case TypeBytes.MESSAGE_TYPE_SERVER: diff --git a/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java b/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java index 71f30297..ce7736da 100644 --- a/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java +++ b/src/main/java/electrosphere/net/parser/net/message/TerrainMessage.java @@ -29,6 +29,7 @@ public class TerrainMessage extends NetworkMessage { SENDFLUIDDATA, UPDATEFLUIDDATA, REQUESTEDITBLOCK, + REQUESTPLACEFAB, } /** @@ -62,6 +63,7 @@ public class TerrainMessage extends NetworkMessage { int blockType; int blockMetadata; int blockEditSize; + String fabPath; /** * Constructor @@ -461,6 +463,20 @@ public class TerrainMessage extends NetworkMessage { this.blockEditSize = blockEditSize; } + /** + * Gets fabPath + */ + public String getfabPath() { + return fabPath; + } + + /** + * Sets fabPath + */ + public void setfabPath(String fabPath) { + this.fabPath = fabPath; + } + /** * Removes the packet header from the buffer * @param byteBuffer The buffer @@ -565,6 +581,8 @@ public class TerrainMessage extends NetworkMessage { } else { return false; } + case TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTPLACEFAB: + return TerrainMessage.canParseRequestPlaceFabMessage(byteBuffer); } return false; } @@ -1262,9 +1280,83 @@ public class TerrainMessage extends NetworkMessage { return rVal; } + /** + * Checks if a message of type RequestPlaceFab can be parsed from the byte stream + */ + public static boolean canParseRequestPlaceFabMessage(CircularByteBuffer byteBuffer){ + int currentStreamLength = byteBuffer.getRemaining(); + List temporaryByteQueue = new LinkedList(); + if(currentStreamLength < 6){ + return false; + } + if(currentStreamLength < 10){ + return false; + } + if(currentStreamLength < 14){ + return false; + } + if(currentStreamLength < 18){ + return false; + } + if(currentStreamLength < 22){ + return false; + } + if(currentStreamLength < 26){ + return false; + } + int fabPathSize = 0; + if(currentStreamLength < 30){ + return false; + } else { + temporaryByteQueue.add(byteBuffer.peek(26 + 0)); + temporaryByteQueue.add(byteBuffer.peek(26 + 1)); + temporaryByteQueue.add(byteBuffer.peek(26 + 2)); + temporaryByteQueue.add(byteBuffer.peek(26 + 3)); + fabPathSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue); + } + if(currentStreamLength < 30 + fabPathSize){ + return false; + } + return true; + } + + /** + * Parses a message of type RequestPlaceFab + */ + public static TerrainMessage parseRequestPlaceFabMessage(CircularByteBuffer byteBuffer, MessagePool pool){ + TerrainMessage rVal = (TerrainMessage)pool.get(MessageType.TERRAIN_MESSAGE); + rVal.messageType = TerrainMessageType.REQUESTPLACEFAB; + TerrainMessage.stripPacketHeader(byteBuffer); + rVal.setworldX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setworldY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setworldZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setvoxelX(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setvoxelY(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setvoxelZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer)); + rVal.setfabPath(ByteStreamUtils.popStringFromByteQueue(byteBuffer)); + return rVal; + } + + /** + * Constructs a message of type RequestPlaceFab + */ + public static TerrainMessage constructRequestPlaceFabMessage(int worldX,int worldY,int worldZ,int voxelX,int voxelY,int voxelZ,String fabPath){ + TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTPLACEFAB); + rVal.setworldX(worldX); + rVal.setworldY(worldY); + rVal.setworldZ(worldZ); + rVal.setvoxelX(voxelX); + rVal.setvoxelY(voxelY); + rVal.setvoxelZ(voxelZ); + rVal.setfabPath(fabPath); + rVal.serialize(); + return rVal; + } + @Override void serialize(){ byte[] intValues = new byte[8]; + byte[] stringBytes; switch(this.messageType){ case REQUESTMETADATA: rawBytes = new byte[2]; @@ -1769,6 +1861,45 @@ public class TerrainMessage extends NetworkMessage { rawBytes[34+i] = intValues[i]; } break; + case REQUESTPLACEFAB: + rawBytes = new byte[2+4+4+4+4+4+4+4+fabPath.length()]; + //message header + rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN; + //entity messaage header + rawBytes[1] = TypeBytes.TERRAIN_MESSAGE_TYPE_REQUESTPLACEFAB; + intValues = ByteStreamUtils.serializeIntToBytes(worldX); + for(int i = 0; i < 4; i++){ + rawBytes[2+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(worldY); + for(int i = 0; i < 4; i++){ + rawBytes[6+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(worldZ); + for(int i = 0; i < 4; i++){ + rawBytes[10+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(voxelX); + for(int i = 0; i < 4; i++){ + rawBytes[14+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(voxelY); + for(int i = 0; i < 4; i++){ + rawBytes[18+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(voxelZ); + for(int i = 0; i < 4; i++){ + rawBytes[22+i] = intValues[i]; + } + intValues = ByteStreamUtils.serializeIntToBytes(fabPath.length()); + for(int i = 0; i < 4; i++){ + rawBytes[26+i] = intValues[i]; + } + stringBytes = fabPath.getBytes(); + for(int i = 0; i < fabPath.length(); i++){ + rawBytes[30+i] = stringBytes[i]; + } + break; } serialized = true; } diff --git a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java index 1c449000..9538d247 100644 --- a/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java +++ b/src/main/java/electrosphere/net/parser/net/message/TypeBytes.java @@ -85,6 +85,7 @@ public class TypeBytes { public static final byte TERRAIN_MESSAGE_TYPE_SENDFLUIDDATA = 15; public static final byte TERRAIN_MESSAGE_TYPE_UPDATEFLUIDDATA = 16; public static final byte TERRAIN_MESSAGE_TYPE_REQUESTEDITBLOCK = 17; + public static final byte TERRAIN_MESSAGE_TYPE_REQUESTPLACEFAB = 18; /* Terrain packet sizes */ diff --git a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java index b82fbae9..50590cd5 100644 --- a/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java +++ b/src/main/java/electrosphere/net/server/protocol/TerrainProtocol.java @@ -92,6 +92,13 @@ public class TerrainProtocol implements ServerProtocolTemplate { Vector3i blockPos = new Vector3i(message.getvoxelX(),message.getvoxelY(),message.getvoxelZ()); ServerBlockEditing.editBlockArea(playerRealm, worldPos, blockPos, (short)message.getblockType(), (short)message.getblockMetadata(), message.getblockEditSize()); } break; + case REQUESTPLACEFAB: { + Vector3i worldPos = new Vector3i(message.getworldX(),message.getworldY(),message.getworldZ()); + Vector3i blockPos = new Vector3i(message.getvoxelX(),message.getvoxelY(),message.getvoxelZ()); + Entity targetEntity = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId()); + Realm playerRealm = Globals.realmManager.getEntityRealm(targetEntity); + ServerBlockEditing.placeBlockFab(playerRealm, worldPos, blockPos, message.getfabPath()); + } break; //all ignored message types case UPDATEFLUIDDATA: case RESPONSEMETADATA: diff --git a/src/main/java/electrosphere/net/synchronization/client/ClientSynchronizationManager.java b/src/main/java/electrosphere/net/synchronization/client/ClientSynchronizationManager.java index e57ffde1..36ff1355 100644 --- a/src/main/java/electrosphere/net/synchronization/client/ClientSynchronizationManager.java +++ b/src/main/java/electrosphere/net/synchronization/client/ClientSynchronizationManager.java @@ -1,6 +1,7 @@ package electrosphere.net.synchronization.client; +import electrosphere.util.Utilities; import electrosphere.entity.state.item.ClientChargeState; import electrosphere.entity.state.movement.editor.ClientEditorMovementTree; import electrosphere.entity.state.equip.ClientToolbarState; diff --git a/src/main/java/electrosphere/server/physics/block/editing/ServerBlockEditing.java b/src/main/java/electrosphere/server/physics/block/editing/ServerBlockEditing.java index 4edb1fa2..1aa234e2 100644 --- a/src/main/java/electrosphere/server/physics/block/editing/ServerBlockEditing.java +++ b/src/main/java/electrosphere/server/physics/block/editing/ServerBlockEditing.java @@ -1,9 +1,12 @@ package electrosphere.server.physics.block.editing; +import java.io.File; + import org.joml.Vector3i; import electrosphere.client.block.BlockChunkData; import electrosphere.controls.cursor.CursorState; +import electrosphere.game.data.block.BlockFab; import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.interfaces.VoxelCellManager; @@ -69,4 +72,37 @@ public class ServerBlockEditing { } } + /** + * Places a block fab + * @param realm The realm + * @param chunkPos The chunk position + * @param voxelPos The voxel position + * @param fabPath The fab + */ + public static void placeBlockFab(Realm realm, Vector3i chunkPos, Vector3i voxelPos, String fabPath){ + BlockFab fab = BlockFab.read(new File(fabPath)); + Vector3i dims = fab.getDimensions(); + Vector3i currChunkPos = new Vector3i(); + Vector3i currVoxelPos = new Vector3i(); + VoxelCellManager voxelCellManager = (VoxelCellManager) realm.getDataCellManager(); + for(int x = 0; x < dims.x; x++){ + for(int y = 0; y < dims.y; y++){ + for(int z = 0; z < dims.z; z++){ + currVoxelPos.set(voxelPos).add(x,y,z); + currChunkPos.set(chunkPos).add( + currVoxelPos.x / BlockChunkData.CHUNK_DATA_WIDTH, + currVoxelPos.y / BlockChunkData.CHUNK_DATA_WIDTH, + currVoxelPos.z / BlockChunkData.CHUNK_DATA_WIDTH + ); + currVoxelPos.set( + currVoxelPos.x % BlockChunkData.CHUNK_DATA_WIDTH, + currVoxelPos.y % BlockChunkData.CHUNK_DATA_WIDTH, + currVoxelPos.z % BlockChunkData.CHUNK_DATA_WIDTH + ); + voxelCellManager.editBlock(currChunkPos, currVoxelPos, fab.getType(x, y, z), (short)0); + } + } + } + } + } diff --git a/src/net/terrain.json b/src/net/terrain.json index f90bc96f..ca9741a0 100644 --- a/src/net/terrain.json +++ b/src/net/terrain.json @@ -128,6 +128,10 @@ { "name" : "blockEditSize", "type" : "FIXED_INT" + }, + { + "name" : "fabPath", + "type" : "VAR_STRING" } ], "messageTypes" : [ @@ -330,6 +334,19 @@ "blockMetadata", "blockEditSize" ] + }, + { + "messageName" : "RequestPlaceFab", + "description" : "Requests that a fab be placed", + "data" : [ + "worldX", + "worldY", + "worldZ", + "voxelX", + "voxelY", + "voxelZ", + "fabPath" + ] } ] }