block synchronization
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2024-06-18 18:41:33 -04:00
parent e671cda62d
commit 8c0a7697d0
45 changed files with 1431 additions and 238 deletions

View File

@ -257,6 +257,7 @@
"firstPersonBone" : "hand.R", "firstPersonBone" : "hand.R",
"offsetVector" : [0,0,0], "offsetVector" : [0,0,0],
"offsetRotation" : [-0.334,0.145,-0.28,0.89], "offsetRotation" : [-0.334,0.145,-0.28,0.89],
"canBlock" : true,
"equipClassWhitelist" : [ "equipClassWhitelist" : [
"tool", "tool",
"weapon", "weapon",
@ -284,6 +285,22 @@
] ]
} }
], ],
"blockSystem" : {
"variants": [
{
"variantId": "blockWeaponRight",
"windUpAnimation" : "Jump",
"mainAnimation" : "Fall",
"cooldownAnimation" : "Land",
"defaults" : [
{
"equipPoint" : "handRight",
"itemClassEquipped" : "weapon"
}
]
}
]
},
"collidable" : { "collidable" : {
"type" : "CYLINDER", "type" : "CYLINDER",
"dimension1" : 0.1, "dimension1" : 0.1,

View File

@ -362,6 +362,9 @@ Fix equipping an item spawning two items
Fix inventory ui not closing when you hit 'i' key (will need to update utility functions to manage input mode so you're not doing it in callback) Fix inventory ui not closing when you hit 'i' key (will need to update utility functions to manage input mode so you're not doing it in callback)
Develop debug ui for equip points Develop debug ui for equip points
(06/18/2024)
Block state synchronization between client and server
# TODO # TODO
@ -375,15 +378,16 @@ Audio FX for everything
= Coding = = Coding =
Sour spot, sweet spot for damage hitboxes and hurtboxes
Fix items falling below the ground Fix items falling below the ground
Control rebinding menu from title screen Sub menu on title screen that allows changing control mappings
Redo hitboxes to have capsules and also chaining between frames (but not between swinging the camera around) Redo hitboxes to have capsules and also chaining between frames (but not between swinging the camera around)
- Introduce block hitbox (blockbox) type - Introduce block hitbox (blockbox) type
Enemy AI Enemy AI
Probably some kind of tutorial text Probably some kind of tutorial text
Network-able ui messages Network-able ui messages
Ability to display video both on title screen as well as in game windows for tutorials Ability to display video both on title screen as well as in game windows for tutorials
better scaffolding for scripting engine with hooks for equipping items, spawning entities, pausing/resuming play, etc better scaffolding for scriptig engine with hooks for equipping items, spawning entities, pausing/resuming play, etc
Ability for private realms to have time start/stop based on the player's feedback <-- sync this up to tutorial ui via script Ability for private realms to have time start/stop based on the player's feedback <-- sync this up to tutorial ui via script

View File

@ -24,6 +24,14 @@
{ {
"name" : "containerType", "name" : "containerType",
"type" : "FIXED_INT" "type" : "FIXED_INT"
},
{
"name" : "itemActionCode",
"type" : "FIXED_INT"
},
{
"name" : "itemActionCodeState",
"type" : "FIXED_INT"
} }
], ],
"messageTypes" : [ "messageTypes" : [
@ -81,6 +89,15 @@
"data" : [ "data" : [
"equipPointId" "equipPointId"
] ]
},
{
"messageName" : "clientRequestPerformItemAction",
"description" : "Requests that the server have the entity perform its equipped item's action for the given equip point",
"data" : [
"equipPointId",
"itemActionCode",
"itemActionCodeState"
]
} }
] ]
} }

View File

@ -0,0 +1,40 @@
package electrosphere.client.item;
import electrosphere.engine.Globals;
import electrosphere.net.parser.net.message.InventoryMessage;
/**
* Utilities for telling the server to perform actions with an item that the player has equipped
*/
public class ItemActions {
//the item action code for left clicking
public static final int ITEM_ACTION_CODE_PRIMARY = 0;
//the item action code for right clicking
public static final int ITEM_ACTION_CODE_SECONDARY = 1;
//the state for performing the item action code
public static final int ITEM_ACTION_CODE_STATE_ON = 1;
//the state for releasing the item action code
public static final int ITEM_ACTION_CODE_STATE_OFF = 0;
/**
* Attempts to perform the secondary item action
*/
public static void attemptSecondaryItemAction(){
//tell the server we want the secondary hand item to START doing something
Globals.clientConnection.queueOutgoingMessage(InventoryMessage.constructclientRequestPerformItemActionMessage("handRight", ITEM_ACTION_CODE_SECONDARY, ITEM_ACTION_CODE_STATE_ON));
//TODO: do any immediate client side calculations here (ie start playing an animation until we get response from server)
}
/**
* Releases the secondary item action
*/
public static void releaseSecondaryItemAction(){
//tell the server we want the secondary hand item to STOP doing something
Globals.clientConnection.queueOutgoingMessage(InventoryMessage.constructclientRequestPerformItemActionMessage("handRight", ITEM_ACTION_CODE_SECONDARY, ITEM_ACTION_CODE_STATE_OFF));
//TODO: do any immediate client side calculations here (ie start playing an animation until we get response from server)
}
}

View File

@ -71,6 +71,7 @@ import org.joml.Vector3d;
import org.joml.Vector3f; import org.joml.Vector3f;
import electrosphere.audio.VirtualAudioSourceManager.VirtualAudioSourceType; import electrosphere.audio.VirtualAudioSourceManager.VirtualAudioSourceType;
import electrosphere.client.item.ItemActions;
import electrosphere.client.targeting.crosshair.Crosshair; import electrosphere.client.targeting.crosshair.Crosshair;
import electrosphere.client.terrain.editing.TerrainEditing; import electrosphere.client.terrain.editing.TerrainEditing;
import electrosphere.collision.CollisionEngine; import electrosphere.collision.CollisionEngine;
@ -85,7 +86,6 @@ import electrosphere.entity.state.attack.ShooterTree;
import electrosphere.entity.state.equip.ClientEquipState; import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.entity.state.inventory.InventoryUtils; import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.state.inventory.UnrelationalInventoryState; import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.entity.state.ironsight.IronSightTree;
import electrosphere.entity.state.movement.JumpTree; import electrosphere.entity.state.movement.JumpTree;
import electrosphere.entity.state.movement.SprintTree; import electrosphere.entity.state.movement.SprintTree;
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree; import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
@ -130,7 +130,7 @@ public class ControlHandler {
public static final String INPUT_CODE_DROP = "drop"; public static final String INPUT_CODE_DROP = "drop";
public static final String INPUT_CODE_INVENTORY_OPEN = "inventoryOpen"; public static final String INPUT_CODE_INVENTORY_OPEN = "inventoryOpen";
public static final String INPUT_CODE_CHARACTER_OPEN = "characterOpen"; public static final String INPUT_CODE_CHARACTER_OPEN = "characterOpen";
public static final String INPUT_CODE_IRON_SIGHT = "ironSight"; public static final String ITEM_SECONDARY = "actionItemSecondary";
public static final String INPUT_CODE_PLACE_TERRAIN = "placeTerrain"; public static final String INPUT_CODE_PLACE_TERRAIN = "placeTerrain";
public static final String INPUT_CODE_REMOVE_TERRAIN = "removeTerrain"; public static final String INPUT_CODE_REMOVE_TERRAIN = "removeTerrain";
@ -312,7 +312,7 @@ public class ControlHandler {
handler.addControl(INPUT_CODE_DROP, new Control(ControlType.KEY,GLFW_KEY_Y)); handler.addControl(INPUT_CODE_DROP, new Control(ControlType.KEY,GLFW_KEY_Y));
handler.addControl(INPUT_CODE_INVENTORY_OPEN, new Control(ControlType.KEY,GLFW_KEY_I)); handler.addControl(INPUT_CODE_INVENTORY_OPEN, new Control(ControlType.KEY,GLFW_KEY_I));
handler.addControl(INPUT_CODE_CHARACTER_OPEN, new Control(ControlType.KEY,GLFW_KEY_C)); handler.addControl(INPUT_CODE_CHARACTER_OPEN, new Control(ControlType.KEY,GLFW_KEY_C));
handler.addControl(INPUT_CODE_IRON_SIGHT, new Control(ControlType.MOUSE_BUTTON,GLFW_MOUSE_BUTTON_RIGHT)); handler.addControl(ITEM_SECONDARY, new Control(ControlType.MOUSE_BUTTON,GLFW_MOUSE_BUTTON_RIGHT));
/* /*
Map the menu navigation controls Map the menu navigation controls
@ -837,22 +837,12 @@ public class ControlHandler {
} }
}}); }});
mainGameControlList.add(controls.get(INPUT_CODE_IRON_SIGHT)); mainGameControlList.add(controls.get(ITEM_SECONDARY));
controls.get(INPUT_CODE_IRON_SIGHT).setOnPress(new ControlMethod() {public void execute() { controls.get(ITEM_SECONDARY).setOnPress(new ControlMethod() {public void execute() {
if(Globals.playerEntity != null){ ItemActions.attemptSecondaryItemAction();
IronSightTree ironSightTree = IronSightTree.getIronSightTree(Globals.playerEntity);
if(ironSightTree != null){
ironSightTree.start();
}
}
}}); }});
controls.get(INPUT_CODE_IRON_SIGHT).setOnRelease(new ControlMethod() {public void execute() { controls.get(ITEM_SECONDARY).setOnRelease(new ControlMethod() {public void execute() {
if(Globals.playerEntity != null){ ItemActions.releaseSecondaryItemAction();
IronSightTree ironSightTree = IronSightTree.getIronSightTree(Globals.playerEntity);
if(ironSightTree != null){
ironSightTree.release();
}
}
}}); }});
/* /*

View File

@ -20,6 +20,7 @@ import electrosphere.game.server.world.MacroData;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.menu.debug.ImGuiWindowMacros; import electrosphere.menu.debug.ImGuiWindowMacros;
import electrosphere.renderer.RenderingEngine; import electrosphere.renderer.RenderingEngine;
import electrosphere.server.MainServerFunctions;
import electrosphere.server.simulation.MacroSimulation; import electrosphere.server.simulation.MacroSimulation;
@ -319,21 +320,10 @@ public class Main {
/// ///
/// S E R V E R M I C R O S I M U L A T I O N /// S E R V E R M A I N R O U T I N E S
/// ///
Globals.profiler.beginCpuSample("Server simulation"); Globals.profiler.beginCpuSample("Main Server Functions");
LoggerInterface.loggerEngine.DEBUG("Begin server micro simulation"); MainServerFunctions.simulate();
if(Globals.realmManager != null){
Globals.realmManager.simulate();
}
///
/// M A C R O S I M U L A T I O N S T U F F
///
LoggerInterface.loggerEngine.DEBUG("Begin server macro simulation");
if(Globals.macroSimulation != null && Globals.macroSimulation.isReady()){
Globals.macroSimulation.simulate();
}
Globals.profiler.endCpuSample(); Globals.profiler.endCpuSample();
} }

View File

@ -272,6 +272,12 @@ public class EntityDataStrings {
*/ */
public static final String IRON_SIGHT_TREE = "ironSightTree"; public static final String IRON_SIGHT_TREE = "ironSightTree";
/*
* Block trees
*/
public static final String TREE_CLIENTBLOCKTREE = "treeClientBlockTree";
public static final String TREE_SERVERBLOCKTREE = "treeServerBlockTree";
/* /*
AI stuff AI stuff
*/ */

View File

@ -1,27 +1,224 @@
package electrosphere.entity.state.block; package electrosphere.entity.state.block;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.game.data.creature.type.block.BlockSystem;
import electrosphere.net.synchronization.BehaviorTreeIdEnums;
import electrosphere.net.synchronization.annotation.SyncedField; import electrosphere.net.synchronization.annotation.SyncedField;
import electrosphere.net.synchronization.annotation.SynchronizableEnum; import electrosphere.net.synchronization.annotation.SynchronizableEnum;
import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree; import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree;
import electrosphere.renderer.actor.Actor;
@SynchronizedBehaviorTree(name = "clientBlockTree", isServer = false, correspondingTree="serverBlockTree") @SynchronizedBehaviorTree(name = "clientBlockTree", isServer = false, correspondingTree="serverBlockTree")
/** /**
* Client block tree * Client block tree
*/ */
public class ClientBlockTree { public class ClientBlockTree implements BehaviorTree {
@SynchronizableEnum @SynchronizableEnum
/** /**
* The state of the block tree * The state of the block tree
*/ */
public enum BlockState { public enum BlockState {
WIND_UP,
BLOCKING, BLOCKING,
COOLDOWN,
NOT_BLOCKING, NOT_BLOCKING,
} }
@SyncedField @SyncedField
BlockState state; BlockState state = BlockState.NOT_BLOCKING; //the current state
//the parent entity to this tree
Entity parent;
@SyncedField
String currentBlockVariant = null; //The current block variant (depends on equipped items)
//The data for block animations
BlockSystem blockSystem;
/**
* Constructor
*/
private ClientBlockTree(Entity parent, BlockSystem blockSystem){
this.parent = parent;
this.blockSystem = blockSystem;
}
@Override
public void simulate(float deltaTime) {
Actor actor = EntityUtils.getActor(parent);
switch(state){
case WIND_UP: {
if(actor != null && blockSystem.getBlockVariant(currentBlockVariant) != null){
String animationToPlay = blockSystem.getBlockVariant(currentBlockVariant).getWindUpAnimation();
if(
!actor.isPlayingAnimation() || !actor.isPlayingAnimation(animationToPlay)
){
actor.playAnimation(animationToPlay,1);
actor.incrementAnimationTime(0.0001);
}
}
} break;
case BLOCKING: {
if(actor != null && blockSystem.getBlockVariant(currentBlockVariant) != null){
String animationToPlay = blockSystem.getBlockVariant(currentBlockVariant).getMainAnimation();
if(
!actor.isPlayingAnimation() || !actor.isPlayingAnimation(animationToPlay)
){
actor.playAnimation(animationToPlay,1);
actor.incrementAnimationTime(0.0001);
}
}
} break;
case COOLDOWN: {
if(actor != null && blockSystem.getBlockVariant(currentBlockVariant) != null){
String animationToPlay = blockSystem.getBlockVariant(currentBlockVariant).getCooldownAnimation();
if(
!actor.isPlayingAnimation() || !actor.isPlayingAnimation(animationToPlay)
){
actor.playAnimation(animationToPlay,1);
actor.incrementAnimationTime(0.0001);
}
}
} break;
case NOT_BLOCKING: {
} break;
}
}
/**
* <p> Automatically generated </p>
* <p>
* Gets state.
* </p>
*/
public BlockState getState(){
return state;
}
/**
* <p> Automatically generated </p>
* <p>
* Sets state and handles the synchronization logic for it.
* </p>
* @param state The value to set state to.
*/
public void setState(BlockState state){
this.state = state;
}
/**
* <p> (initially) Automatically generated </p>
* <p> More parameters can be safely added to this method</p>
* <p>
* Attaches this tree to the entity.
* </p>
* @param entity The entity to attach to
* @param tree The behavior tree to attach
*/
public static ClientBlockTree attachTree(Entity parent, BlockSystem blockSystem){
ClientBlockTree rVal = new ClientBlockTree(parent, blockSystem);
//put manual code here (setting params, etc)
//!!WARNING!! from here below should not be touched
//This was generated automatically to properly alert various systems that the btree exists and should be tracked
parent.putData(EntityDataStrings.TREE_CLIENTBLOCKTREE, rVal);
Globals.clientSceneWrapper.getScene().registerBehaviorTree(rVal);
Globals.entityValueTrackingService.attachTreeToEntity(parent, BehaviorTreeIdEnums.BTREE_CLIENTBLOCKTREE_ID);
return rVal;
}
/**
* <p> Automatically generated </p>
* <p>
* Detatches this tree from the entity.
* </p>
* @param entity The entity to detach to
* @param tree The behavior tree to detach
*/
public static void detachTree(Entity entity, BehaviorTree tree){
Globals.entityValueTrackingService.detatchTreeFromEntity(entity, BehaviorTreeIdEnums.BTREE_CLIENTBLOCKTREE_ID);
}
/**
* <p>
* Gets the ClientBlockTree of the entity
* </p>
* @param entity the entity
* @return The ClientBlockTree
*/
public static ClientBlockTree getClientBlockTree(Entity entity){
return (ClientBlockTree)entity.getData(EntityDataStrings.TREE_CLIENTBLOCKTREE);
}
/**
* <p> Automatically generated </p>
* <p>
* Converts this enum type to an equivalent short value
* </p>
* @param enumVal The enum value
* @return The short value
*/
public static short getBlockStateEnumAsShort(BlockState enumVal){
switch(enumVal){
case WIND_UP:
return 0;
case BLOCKING:
return 1;
case COOLDOWN:
return 2;
case NOT_BLOCKING:
return 3;
default:
return 0;
}
}
/**
* <p> Automatically generated </p>
* <p>
* Converts a short to the equivalent enum value
* </p>
* @param shortVal The short value
* @return The enum value
*/
public static BlockState getBlockStateShortAsEnum(short shortVal){
switch(shortVal){
case 0:
return BlockState.WIND_UP;
case 1:
return BlockState.BLOCKING;
case 2:
return BlockState.COOLDOWN;
case 3:
return BlockState.NOT_BLOCKING;
default:
return BlockState.WIND_UP;
}
}
/**
* <p> Automatically generated </p>
* <p>
* Gets currentBlockVariant.
* </p>
*/
public String getCurrentBlockVariant(){
return currentBlockVariant;
}
/**
* <p> Automatically generated </p>
* <p>
* Sets currentBlockVariant and handles the synchronization logic for it.
* </p>
* @param currentBlockVariant The value to set currentBlockVariant to.
*/
public void setCurrentBlockVariant(String currentBlockVariant){
this.currentBlockVariant = currentBlockVariant;
}
} }

View File

@ -1,6 +1,21 @@
package electrosphere.entity.state.block; package electrosphere.entity.state.block;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.net.synchronization.BehaviorTreeIdEnums;
import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils;
import electrosphere.server.poseactor.PoseActor;
import electrosphere.net.parser.net.message.SynchronizationMessage;
import electrosphere.server.datacell.utils.DataCellSearchUtils;
import electrosphere.entity.state.block.ClientBlockTree.BlockState; import electrosphere.entity.state.block.ClientBlockTree.BlockState;
import electrosphere.game.data.creature.type.block.BlockSystem;
import electrosphere.net.synchronization.annotation.SyncedField; import electrosphere.net.synchronization.annotation.SyncedField;
import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree; import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree;
@ -8,9 +23,175 @@ import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree;
/** /**
* Server block tree * Server block tree
*/ */
public class ServerBlockTree { public class ServerBlockTree implements BehaviorTree {
@SyncedField @SyncedField
BlockState state; BlockState state = BlockState.NOT_BLOCKING; //the current state of the tree
//the parent entity to this tree
Entity parent;
@SyncedField
String currentBlockVariant = null; //The current block variant (depends on equipped items)
//The data for block animations
BlockSystem blockSystem;
/**
* Constructor
*/
private ServerBlockTree(Entity parent, BlockSystem blockSystem){
this.parent = parent;
this.blockSystem = blockSystem;
}
/**
* Starts the block tree
*/
public void start(){
setState(BlockState.WIND_UP);
}
/**
* Stops the block tree
*/
public void stop(){
setState(BlockState.COOLDOWN);
}
@Override
public void simulate(float deltaTime) {
PoseActor poseActor = EntityUtils.getPoseActor(parent);
switch(state){
case WIND_UP: {
if(poseActor != null && blockSystem.getBlockVariant(currentBlockVariant) != null){
String animationToPlay = blockSystem.getBlockVariant(currentBlockVariant).getWindUpAnimation();
if(
!poseActor.isPlayingAnimation() || !poseActor.isPlayingAnimation(animationToPlay)
){
poseActor.playAnimation(animationToPlay,1);
poseActor.incrementAnimationTime(0.0001);
}
}
} break;
case BLOCKING: {
if(poseActor != null && blockSystem.getBlockVariant(currentBlockVariant) != null){
String animationToPlay = blockSystem.getBlockVariant(currentBlockVariant).getMainAnimation();
if(
!poseActor.isPlayingAnimation() || !poseActor.isPlayingAnimation(animationToPlay)
){
poseActor.playAnimation(animationToPlay,1);
poseActor.incrementAnimationTime(0.0001);
}
}
} break;
case COOLDOWN: {
if(poseActor != null && blockSystem.getBlockVariant(currentBlockVariant) != null){
String animationToPlay = blockSystem.getBlockVariant(currentBlockVariant).getCooldownAnimation();
if(
!poseActor.isPlayingAnimation() || !poseActor.isPlayingAnimation(animationToPlay)
){
poseActor.playAnimation(animationToPlay,1);
poseActor.incrementAnimationTime(0.0001);
}
}
} break;
case NOT_BLOCKING: {
} break;
}
}
/**
* Gets the block system data for this tree
* @return the data if it exists, otherwise null
*/
public BlockSystem getBlockSystem(){
return this.blockSystem;
}
/**
* <p> Automatically generated </p>
* <p>
* Gets state.
* </p>
*/
public BlockState getState(){
return state;
}
/**
* <p> Automatically generated </p>
* <p>
* Sets state and handles the synchronization logic for it.
* </p>
* @param state The value to set state to.
*/
public void setState(BlockState state){
this.state = state;
int value = ClientBlockTree.getBlockStateEnumAsShort(state);
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructUpdateClientStateMessage(parent.getId(), 3, 8, value));
}
/**
* <p> (initially) Automatically generated </p>
* <p> More parameters can be safely added to this method</p>
* <p>
* Attaches this tree to the entity.
* </p>
* @param entity The entity to attach to
* @param tree The behavior tree to attach
*/
public static ServerBlockTree attachTree(Entity parent, BlockSystem blockSystem){
ServerBlockTree rVal = new ServerBlockTree(parent, blockSystem);
//put manual code here (setting params, etc)
//!!WARNING!! from here below should not be touched
//This was generated automatically to properly alert various systems that the btree exists and should be tracked
ServerBehaviorTreeUtils.attachBTreeToEntity(parent, rVal);
parent.putData(EntityDataStrings.TREE_SERVERBLOCKTREE, rVal);
Globals.entityValueTrackingService.attachTreeToEntity(parent, BehaviorTreeIdEnums.BTREE_SERVERBLOCKTREE_ID);
return rVal;
}
/**
* <p> Automatically generated </p>
* <p>
* Detatches this tree from the entity.
* </p>
* @param entity The entity to detach to
* @param tree The behavior tree to detach
*/
public static void detachTree(Entity entity, BehaviorTree tree){
Globals.entityValueTrackingService.detatchTreeFromEntity(entity, BehaviorTreeIdEnums.BTREE_SERVERBLOCKTREE_ID);
}
/**
* <p>
* Gets the ServerBlockTree of the entity
* </p>
* @param entity the entity
* @return The ServerBlockTree
*/
public static ServerBlockTree getServerBlockTree(Entity entity){
return (ServerBlockTree)entity.getData(EntityDataStrings.TREE_SERVERBLOCKTREE);
}
/**
* <p> Automatically generated </p>
* <p>
* Gets currentBlockVariant.
* </p>
*/
public String getCurrentBlockVariant(){
return currentBlockVariant;
}
/**
* <p> Automatically generated </p>
* <p>
* Sets currentBlockVariant and handles the synchronization logic for it.
* </p>
* @param currentBlockVariant The value to set currentBlockVariant to.
*/
public void setCurrentBlockVariant(String currentBlockVariant){
this.currentBlockVariant = currentBlockVariant;
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructUpdateClientStringStateMessage(parent.getId(), 3, 9, currentBlockVariant));
}
} }

View File

@ -107,8 +107,11 @@ public class ClientEquipState implements BehaviorTree {
List<String> pointEquipClassList = point.getEquipClassWhitelist(); List<String> pointEquipClassList = point.getEquipClassWhitelist();
boolean itemIsInPointWhitelist = pointEquipClassList.contains(equipItemClass); boolean itemIsInPointWhitelist = pointEquipClassList.contains(equipItemClass);
if(!hasEquipped && targetIsItem && !targetIsAttached && itemIsInPointWhitelist){ if(!hasEquipped && targetIsItem && !targetIsAttached && itemIsInPointWhitelist){
//
//visual transforms
if(targetHasWhitelist){ if(targetHasWhitelist){
//by attaching are we going to be replacing meshes? //depends on the type of creature, must be replacing a mesh
String parentCreatureId = CreatureUtils.getType(parent); String parentCreatureId = CreatureUtils.getType(parent);
List<EquipWhitelist> whitelist = ItemUtils.getEquipWhitelist(toEquip); List<EquipWhitelist> whitelist = ItemUtils.getEquipWhitelist(toEquip);
for(EquipWhitelist whitelistItem : whitelist){ for(EquipWhitelist whitelistItem : whitelist){
@ -141,7 +144,7 @@ public class ClientEquipState implements BehaviorTree {
} }
} }
} else { } else {
//since we're not replacing meshes we must be attaching to a bone //does not depend on the type of creature, must be attaching to a bone
equipMap.put(point.getEquipPointId(),toEquip); equipMap.put(point.getEquipPointId(),toEquip);
if(Globals.controlHandler.cameraIsThirdPerson()){ if(Globals.controlHandler.cameraIsThirdPerson()){
AttachUtils.clientAttachEntityToEntityAtBone(parent, toEquip, point.getBone(), AttachUtils.getEquipPointRotationOffset(point.getOffsetRotation())); AttachUtils.clientAttachEntityToEntityAtBone(parent, toEquip, point.getBone(), AttachUtils.getEquipPointRotationOffset(point.getOffsetRotation()));
@ -284,8 +287,11 @@ public class ClientEquipState implements BehaviorTree {
Entity equipped = equipMap.remove(pointId); Entity equipped = equipMap.remove(pointId);
if(equipped != null){ if(equipped != null){
boolean targetHasWhitelist = ItemUtils.hasEquipList(equipped); boolean targetHasWhitelist = ItemUtils.hasEquipList(equipped);
//
//visual transforms
if(targetHasWhitelist){ if(targetHasWhitelist){
//by attaching are we going to be replacing meshes? //depends on the type of creature, must be replacing meshes
String parentCreatureId = CreatureUtils.getType(parent); String parentCreatureId = CreatureUtils.getType(parent);
List<EquipWhitelist> whitelist = ItemUtils.getEquipWhitelist(equipped); List<EquipWhitelist> whitelist = ItemUtils.getEquipWhitelist(equipped);
for(EquipWhitelist whitelistItem : whitelist){ for(EquipWhitelist whitelistItem : whitelist){
@ -304,6 +310,7 @@ public class ClientEquipState implements BehaviorTree {
} }
} }
} else { } else {
//does not depend on the type of creature
AttachUtils.clientDetatchEntityFromEntityAtBone(parent, equipped); AttachUtils.clientDetatchEntityFromEntityAtBone(parent, equipped);
EntityUtils.cleanUpEntity(equipped); EntityUtils.cleanUpEntity(equipped);
} }

View File

@ -21,6 +21,7 @@ import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags; import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils; import electrosphere.entity.EntityUtils;
import electrosphere.entity.btree.BehaviorTree; import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.state.block.ServerBlockTree;
import electrosphere.entity.state.gravity.GravityUtils; import electrosphere.entity.state.gravity.GravityUtils;
import electrosphere.entity.state.inventory.InventoryUtils; import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.state.inventory.RelationalInventoryState; import electrosphere.entity.state.inventory.RelationalInventoryState;
@ -28,6 +29,8 @@ import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.entity.types.attach.AttachUtils; import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.entity.types.creature.CreatureUtils; import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.entity.types.item.ItemUtils; import electrosphere.entity.types.item.ItemUtils;
import electrosphere.game.data.creature.type.block.BlockSystem;
import electrosphere.game.data.creature.type.block.BlockVariant;
import electrosphere.game.data.creature.type.equip.EquipPoint; import electrosphere.game.data.creature.type.equip.EquipPoint;
import electrosphere.game.data.item.type.EquipWhitelist; import electrosphere.game.data.item.type.EquipWhitelist;
import electrosphere.net.parser.net.message.InventoryMessage; import electrosphere.net.parser.net.message.InventoryMessage;
@ -46,9 +49,12 @@ import electrosphere.server.datacell.utils.ServerEntityTagUtils;
*/ */
public class ServerEquipState implements BehaviorTree { public class ServerEquipState implements BehaviorTree {
//the parent entity of this equip state
Entity parent; Entity parent;
//the list of available equip points
List<EquipPoint> equipPoints = new LinkedList<EquipPoint>(); List<EquipPoint> equipPoints = new LinkedList<EquipPoint>();
//the map of equip point id -> entity equipped at said point
Map<String,Entity> equipMap = new HashMap<String,Entity>(); Map<String,Entity> equipMap = new HashMap<String,Entity>();
public ServerEquipState(Entity parent, List<EquipPoint> equipPoints){ public ServerEquipState(Entity parent, List<EquipPoint> equipPoints){
@ -76,6 +82,11 @@ public class ServerEquipState implements BehaviorTree {
} }
} }
/**
* Attempts to equip an item
* @param inInventoryEntity The item to equip
* @param point The point to equip to
*/
public void serverAttemptEquip(Entity inInventoryEntity, EquipPoint point){ public void serverAttemptEquip(Entity inInventoryEntity, EquipPoint point){
boolean hasEquipped = hasEquippedAtPoint(point.getEquipPointId()); boolean hasEquipped = hasEquippedAtPoint(point.getEquipPointId());
boolean targetIsItem = ItemUtils.isItem(inInventoryEntity); boolean targetIsItem = ItemUtils.isItem(inInventoryEntity);
@ -91,8 +102,10 @@ public class ServerEquipState implements BehaviorTree {
//bind in world with in inventory //bind in world with in inventory
ItemUtils.setRealWorldEntity(inInventoryEntity, inWorldItem); ItemUtils.setRealWorldEntity(inInventoryEntity, inWorldItem);
//
//Visual transforms
if(targetHasWhitelist){ if(targetHasWhitelist){
//by attaching are we going to be replacing meshes? //depends on the type of creature
String parentCreatureId = CreatureUtils.getType(parent); String parentCreatureId = CreatureUtils.getType(parent);
List<EquipWhitelist> whitelist = ItemUtils.getEquipWhitelist(inWorldItem); List<EquipWhitelist> whitelist = ItemUtils.getEquipWhitelist(inWorldItem);
for(EquipWhitelist whitelistItem : whitelist){ for(EquipWhitelist whitelistItem : whitelist){
@ -117,7 +130,7 @@ public class ServerEquipState implements BehaviorTree {
} }
} }
} else { } else {
//since we're not replacing meshes we must be attaching to a bone //does not depend on the type of creature
equipMap.put(point.getEquipPointId(),inWorldItem); equipMap.put(point.getEquipPointId(),inWorldItem);
AttachUtils.serverAttachEntityToEntityAtBone(parent, inWorldItem, point.getBone(), AttachUtils.getEquipPointRotationOffset(point.getOffsetRotation())); AttachUtils.serverAttachEntityToEntityAtBone(parent, inWorldItem, point.getBone(), AttachUtils.getEquipPointRotationOffset(point.getOffsetRotation()));
if(inWorldItem.containsKey(EntityDataStrings.PHYSICS_COLLISION_BODY) && inWorldItem.containsKey(EntityDataStrings.PHYSICS_COLLIDABLE)){ if(inWorldItem.containsKey(EntityDataStrings.PHYSICS_COLLISION_BODY) && inWorldItem.containsKey(EntityDataStrings.PHYSICS_COLLIDABLE)){
@ -128,6 +141,11 @@ public class ServerEquipState implements BehaviorTree {
ServerEntityTagUtils.removeTagFromEntity(inWorldItem, EntityTags.TARGETABLE); ServerEntityTagUtils.removeTagFromEntity(inWorldItem, EntityTags.TARGETABLE);
GravityUtils.serverAttemptDeactivateGravity(inWorldItem); GravityUtils.serverAttemptDeactivateGravity(inWorldItem);
} }
//
//update block state based on what we have equipped
this.updateBlockVariant();
//we need to send two packets //we need to send two packets
//1) Remove item from original inventory //1) Remove item from original inventory
//2) Add item with ID to "equipped" inventory //2) Add item with ID to "equipped" inventory
@ -171,6 +189,11 @@ public class ServerEquipState implements BehaviorTree {
} }
} }
/**
* Gets an equip point by its name
* @param name The name of the equip point
* @return The equip point if it exists, null otherwise
*/
public EquipPoint getEquipPoint(String name){ public EquipPoint getEquipPoint(String name){
for(EquipPoint point : equipPoints){ for(EquipPoint point : equipPoints){
if(point.getEquipPointId().equals(name)){ if(point.getEquipPointId().equals(name)){
@ -180,6 +203,11 @@ public class ServerEquipState implements BehaviorTree {
return null; return null;
} }
/**
* Gets the item equipped at a point
* @param point The point id
* @return The item if it exists, null otherwise
*/
public Entity getEquippedItemAtPoint(String point){ public Entity getEquippedItemAtPoint(String point){
return equipMap.get(point); return equipMap.get(point);
} }
@ -223,6 +251,10 @@ public class ServerEquipState implements BehaviorTree {
// } // }
// } // }
/**
* Commands the equip state to unequip an item at a given equip point
* @param pointId The equip point
*/
public void commandAttemptUnequip(String pointId){ public void commandAttemptUnequip(String pointId){
boolean hasEquipped = hasEquippedAtPoint(pointId); boolean hasEquipped = hasEquippedAtPoint(pointId);
if(hasEquipped){ if(hasEquipped){
@ -278,10 +310,17 @@ public class ServerEquipState implements BehaviorTree {
// inventory.addItem(item); // inventory.addItem(item);
} }
/**
* Performs the transform to unequip an item from an equip point
* @param pointId The equip point id
*/
public void serverTransformUnequipPoint(String pointId){ public void serverTransformUnequipPoint(String pointId){
Entity equipped = equipMap.remove(pointId); Entity equipped = equipMap.remove(pointId);
if(equipped != null){ if(equipped != null){
boolean targetHasWhitelist = ItemUtils.hasEquipList(equipped); boolean targetHasWhitelist = ItemUtils.hasEquipList(equipped);
//
//Visual transforms
if(targetHasWhitelist){ if(targetHasWhitelist){
//have to do fancy mesh removal nonsense //have to do fancy mesh removal nonsense
//basically the reverse of below //basically the reverse of below
@ -319,13 +358,50 @@ public class ServerEquipState implements BehaviorTree {
AttachUtils.serverDetatchEntityFromEntityAtBone(parent, equipped); AttachUtils.serverDetatchEntityFromEntityAtBone(parent, equipped);
EntityUtils.cleanUpEntity(equipped); EntityUtils.cleanUpEntity(equipped);
} }
//
//update block state based on what we have equipped
this.updateBlockVariant();
} }
} }
/**
* Gets whether an item is equipped at a point or not
* @param point THe point to check
* @return true if an item is equipped at the point, false otherwise
*/
public boolean hasEquippedAtPoint(String point){ public boolean hasEquippedAtPoint(String point){
return equipMap.containsKey(point); return equipMap.containsKey(point);
} }
/**
* Updates the server block variant based on what item is equipped
*/
private void updateBlockVariant(){
ServerBlockTree blockTree = ServerBlockTree.getServerBlockTree(parent);
if(blockTree != null){
List<EquipPoint> pointsThatCanBlock = new LinkedList<EquipPoint>();
for(EquipPoint point : equipPoints){
if(point.getCanBlock()){
pointsThatCanBlock.add(point);
}
}
BlockSystem blockData = blockTree.getBlockSystem();
for(EquipPoint point : pointsThatCanBlock){
Entity item = getEquippedItemAtPoint(point.getEquipPointId());
if(item != null){
BlockVariant blockVariant = blockData.getVariantForPointWithItem(point.getEquipPointId(),ItemUtils.getEquipClass(item));
//TODO: refactor to allow sending more than one variant at a time
//ie if you have two items equipped and you want to block with both
blockTree.setCurrentBlockVariant(blockVariant.getVariantId());
}
}
}
}
@Override @Override
public void simulate(float deltaTime) { public void simulate(float deltaTime) {
} }

View File

@ -215,7 +215,7 @@ public class ServerGravityTree implements BehaviorTree {
public void setState(GravityTreeState state){ public void setState(GravityTreeState state){
this.state = state; this.state = state;
int value = ClientGravityTree.getGravityTreeStateEnumAsShort(state); int value = ClientGravityTree.getGravityTreeStateEnumAsShort(state);
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructUpdateClientStateMessage(parent.getId(), 5, 7, value)); DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructUpdateClientStateMessage(parent.getId(), 7, 11, value));
} }
/** /**
* <p> (initially) Automatically generated </p> * <p> (initially) Automatically generated </p>

View File

@ -215,4 +215,14 @@ public class ClientIdleTree implements BehaviorTree {
public void setState(IdleTreeState state){ public void setState(IdleTreeState state){
this.state = state; this.state = state;
} }
/**
* <p>
* Gets the ClientIdleTree of the entity
* </p>
* @param entity the entity
* @return The ClientIdleTree
*/
public static ClientIdleTree getClientIdleTree(Entity entity){
return (ClientIdleTree)entity.getData(EntityDataStrings.TREE_IDLE);
}
} }

View File

@ -190,7 +190,7 @@ public class ServerIdleTree implements BehaviorTree {
public void setState(IdleTreeState state){ public void setState(IdleTreeState state){
this.state = state; this.state = state;
int value = ClientIdleTree.getIdleTreeStateEnumAsShort(state); int value = ClientIdleTree.getIdleTreeStateEnumAsShort(state);
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructUpdateClientStateMessage(parent.getId(), 7, 9, value)); DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructUpdateClientStateMessage(parent.getId(), 9, 13, value));
} }
/** /**

View File

@ -1,70 +0,0 @@
package electrosphere.entity.state.ironsight;
import org.joml.Vector3f;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.types.camera.CameraEntityUtils;
public class IronSightTree implements BehaviorTree {
static enum IronSightTreeState {
ACTIVE,
INACTIVE,
}
IronSightTreeState state = IronSightTreeState.INACTIVE;
boolean cameraZoomedIn = false;
float regularRadius = 1.0f;
float zoomedInRadius = 0.1f;
Vector3f offcenterOffset = new Vector3f(-0.1f,1,0);
public void start(){
state = IronSightTreeState.ACTIVE;
}
public void release(){
state = IronSightTreeState.INACTIVE;
}
@Override
public void simulate(float deltaTime) {
switch(state){
case ACTIVE:
if(!cameraZoomedIn){
cameraZoomedIn = true;
CameraEntityUtils.setOrbitalCameraDistance(Globals.playerCamera, zoomedInRadius);
Globals.cameraHandler.updateRadialOffset(new Vector3f(0,1,0.5f));
Globals.cameraHandler.updateGlobalCamera();
}
break;
case INACTIVE:
if(cameraZoomedIn){
cameraZoomedIn = false;
CameraEntityUtils.setOrbitalCameraDistance(Globals.playerCamera, regularRadius);
Globals.cameraHandler.updateRadialOffset(new Vector3f(0,1,0));
Globals.cameraHandler.updateGlobalCamera();
}
break;
}
}
public static IronSightTree getIronSightTree(Entity creature){
Object rVal;
if((rVal = creature.getData(EntityDataStrings.IRON_SIGHT_TREE)) != null && rVal instanceof IronSightTree){
return (IronSightTree) rVal;
}
return null;
}
public static void attachIronSightTree(Entity player){
IronSightTree ironSightTree = new IronSightTree();
player.putData(EntityDataStrings.IRON_SIGHT_TREE, ironSightTree);
Globals.clientSceneWrapper.getScene().registerBehaviorTree(ironSightTree);
}
}

View File

@ -1,6 +1,8 @@
package electrosphere.entity.state.movement.groundmove; package electrosphere.entity.state.movement.groundmove;
import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils;
import electrosphere.net.parser.net.message.SynchronizationMessage; import electrosphere.net.parser.net.message.SynchronizationMessage;
import electrosphere.net.synchronization.BehaviorTreeIdEnums; import electrosphere.net.synchronization.BehaviorTreeIdEnums;
@ -676,7 +678,7 @@ public class ServerGroundMovementTree implements BehaviorTree {
public void setFacing(MovementRelativeFacing facing){ public void setFacing(MovementRelativeFacing facing){
this.facing = facing; this.facing = facing;
int value = ClientGroundMovementTree.getMovementRelativeFacingEnumAsShort(facing); int value = ClientGroundMovementTree.getMovementRelativeFacingEnumAsShort(facing);
DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructUpdateClientStateMessage(parent.getId(), 9, 11, value)); DataCellSearchUtils.getEntityDataCell(parent).broadcastNetworkMessage(SynchronizationMessage.constructUpdateClientStateMessage(parent.getId(), 11, 15, value));
} }
/** /**

View File

@ -540,18 +540,42 @@ public class AttachUtils {
return (Matrix4f)e.getData(EntityDataStrings.ATTACH_TRANSFORM); return (Matrix4f)e.getData(EntityDataStrings.ATTACH_TRANSFORM);
} }
/**
* Checks if the parent entity has attached child entities
* @param e The parent entity
* @return true if there are attached child entities, false otherwise
*/
public static boolean hasChildren(Entity e){ public static boolean hasChildren(Entity e){
return e.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST) && !getChildrenList(e).isEmpty(); return e.containsKey(EntityDataStrings.ATTACH_CHILDREN_LIST) && !getChildrenList(e).isEmpty();
} }
/**
* Gets the attached entity's posiiton offset
* @param e The attached entity
* @return The position offset
*/
public static Vector3d getAttachPositionOffset(Entity e){ public static Vector3d getAttachPositionOffset(Entity e){
return (Vector3d)e.getData(EntityDataStrings.ATTACH_POSITION_OFFSET); return (Vector3d)e.getData(EntityDataStrings.ATTACH_POSITION_OFFSET);
} }
public static LinkedList<Entity> getChildrenList(Entity e){ /**
return (LinkedList<Entity>)e.getData(EntityDataStrings.ATTACH_CHILDREN_LIST); * Gets the list of entities attached to this parent entity
* <p>
* NOTE: This can return an empty list of an entity has been attached to this one prior
* EVEN if it has since been unattached
* </p>
* @param parentEntity
* @return The list of entities that are attached to this parent entity, or null if undefined
*/
public static LinkedList<Entity> getChildrenList(Entity parentEntity){
return (LinkedList<Entity>)parentEntity.getData(EntityDataStrings.ATTACH_CHILDREN_LIST);
} }
/**
* Gets the equip point's rotation offset in quaterniond form
* @param values The list of raw float values
* @return The quaterniond containing those values or an identity quaterniond if no such values exist
*/
public static Quaterniond getEquipPointRotationOffset(List<Float> values){ public static Quaterniond getEquipPointRotationOffset(List<Float> values){
if(values.size() > 0){ if(values.size() > 0){
return new Quaterniond(values.get(0),values.get(1),values.get(2),values.get(3)); return new Quaterniond(values.get(0),values.get(1),values.get(2),values.get(3));

View File

@ -24,6 +24,8 @@ import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.state.attack.ClientAttackTree; import electrosphere.entity.state.attack.ClientAttackTree;
import electrosphere.entity.state.attack.ServerAttackTree; import electrosphere.entity.state.attack.ServerAttackTree;
import electrosphere.entity.state.attack.ShooterTree; import electrosphere.entity.state.attack.ShooterTree;
import electrosphere.entity.state.block.ClientBlockTree;
import electrosphere.entity.state.block.ServerBlockTree;
import electrosphere.entity.state.collidable.ClientCollidableTree; import electrosphere.entity.state.collidable.ClientCollidableTree;
import electrosphere.entity.state.collidable.ServerCollidableTree; import electrosphere.entity.state.collidable.ServerCollidableTree;
import electrosphere.entity.state.equip.ClientEquipState; import electrosphere.entity.state.equip.ClientEquipState;
@ -229,6 +231,9 @@ public class CreatureUtils {
ClientEquipState.attachTree(rVal, rawType.getEquipPoints()); ClientEquipState.attachTree(rVal, rawType.getEquipPoints());
rVal.putData(EntityDataStrings.EQUIP_INVENTORY, RelationalInventoryState.buildRelationalInventoryStateFromEquipList(rawType.getEquipPoints())); rVal.putData(EntityDataStrings.EQUIP_INVENTORY, RelationalInventoryState.buildRelationalInventoryStateFromEquipList(rawType.getEquipPoints()));
} }
if(rawType.getBlockSystem() != null){
ClientBlockTree.attachTree(rVal, rawType.getBlockSystem());
}
for(String token : rawType.getTokens()){ for(String token : rawType.getTokens()){
switch(token){ switch(token){
case "BLENDER_TRANSFORM": case "BLENDER_TRANSFORM":
@ -517,6 +522,9 @@ public class CreatureUtils {
ServerEquipState.setEquipState(rVal, new ServerEquipState(rVal,rawType.getEquipPoints())); ServerEquipState.setEquipState(rVal, new ServerEquipState(rVal,rawType.getEquipPoints()));
rVal.putData(EntityDataStrings.EQUIP_INVENTORY, RelationalInventoryState.buildRelationalInventoryStateFromEquipList(rawType.getEquipPoints())); rVal.putData(EntityDataStrings.EQUIP_INVENTORY, RelationalInventoryState.buildRelationalInventoryStateFromEquipList(rawType.getEquipPoints()));
} }
if(rawType.getBlockSystem() != null){
ServerBlockTree.attachTree(rVal, rawType.getBlockSystem());
}
for(String token : rawType.getTokens()){ for(String token : rawType.getTokens()){
switch(token){ switch(token){
case "BLENDER_TRANSFORM": { case "BLENDER_TRANSFORM": {

View File

@ -4,6 +4,7 @@ import electrosphere.game.data.collidable.CollidableTemplate;
import electrosphere.game.data.collidable.HitboxData; import electrosphere.game.data.collidable.HitboxData;
import electrosphere.game.data.creature.type.attack.AttackMove; import electrosphere.game.data.creature.type.attack.AttackMove;
import electrosphere.game.data.creature.type.attack.AttackMoveResolver; import electrosphere.game.data.creature.type.attack.AttackMoveResolver;
import electrosphere.game.data.creature.type.block.BlockSystem;
import electrosphere.game.data.creature.type.equip.EquipPoint; import electrosphere.game.data.creature.type.equip.EquipPoint;
import electrosphere.game.data.creature.type.movement.MovementSystem; import electrosphere.game.data.creature.type.movement.MovementSystem;
import electrosphere.game.data.creature.type.rotator.RotatorSystem; import electrosphere.game.data.creature.type.rotator.RotatorSystem;
@ -29,6 +30,7 @@ public class CreatureType {
String modelPath; String modelPath;
ViewModelData viewModelData; ViewModelData viewModelData;
IdleData idleData; IdleData idleData;
BlockSystem blockSystem;
AttackMoveResolver attackMoveResolver; AttackMoveResolver attackMoveResolver;
@ -95,6 +97,10 @@ public class CreatureType {
public IdleData getIdleData(){ public IdleData getIdleData(){
return idleData; return idleData;
} }
public BlockSystem getBlockSystem(){
return blockSystem;
}
} }

View File

@ -0,0 +1,56 @@
package electrosphere.game.data.creature.type.block;
import java.util.List;
/**
* Stores data related to an entity blocking attacks
*/
public class BlockSystem {
//blocking with a weapon equipped in the right hand
//NOTE: the names provided here should line up with the actual field names on this object
public static final String BLOCK_VARIANT_WEAPON_RIGHT = "blockWeaponRight";
//the list of block variants
List<BlockVariant> variants;
/**
* Gets the list of block variants
* @return the list
*/
public List<BlockVariant> getAllVariants(){
return variants;
}
/**
* Gets a block variant from its variant string
* @param variantString The variant string
* @return The block variant if it exists, null otherwise
*/
public BlockVariant getBlockVariant(String variantString){
for(BlockVariant variant : variants){
if(variant.variantId.equals(variantString)){
return variant;
}
}
return null;
}
/**
* Gets the block variant that is default for the provided equip point when the provided item class is equipped to that point
* @param equipPoint The equip point
* @param itemClass The item class
* @return The block variant if it exists, null otherwise
*/
public BlockVariant getVariantForPointWithItem(String equipPoint, String itemClass){
for(BlockVariant variant : variants){
for(VariantDefaults variantDefault : variant.getDefaults()){
if(variantDefault.equipPoint.equals(equipPoint) && variantDefault.itemClassEquipped.equals(itemClass)){
return variant;
}
}
}
return null;
}
}

View File

@ -0,0 +1,67 @@
package electrosphere.game.data.creature.type.block;
import java.util.List;
/**
* A variant of data that can be loaded into the block system. Variants are for different types of equip states.
* IE: holding just a sword in your right hand will have a different block animation vs a shield in your right
* hand vs two handing a sword with your right hand.
*/
public class BlockVariant {
//The id of the block variant
String variantId;
//the animation to play when winding up
String windUpAnimation;
//the main animation to play while blocking
String mainAnimation;
//the animation to play when cooling down
String cooldownAnimation;
//the list of default equipment cases that this variant should be used for
List<VariantDefaults> defaults;
/**
* The id of the block variant
* @return
*/
public String getVariantId(){
return variantId;
}
/**
* The animation to play when winding up
* @return
*/
public String getWindUpAnimation(){
return windUpAnimation;
}
/**
* The main animation to play while blocking
* @return
*/
public String getMainAnimation(){
return mainAnimation;
}
/**
* The animation to play when cooling down
* @return
*/
public String getCooldownAnimation(){
return cooldownAnimation;
}
/**
* the list of default equipment cases that this variant should be used for
* @return
*/
public List<VariantDefaults> getDefaults(){
return defaults;
}
}

View File

@ -0,0 +1,34 @@
package electrosphere.game.data.creature.type.block;
/**
* Equip point cases that this variant is used as the default for
* IE, if you create a variant default for "handRight, weapon",
* that means this should be used as the default variant for when
* the handRight equip point has a weapon equipped and the block
* tree is triggered
*/
public class VariantDefaults {
//the equip point
String equipPoint;
//the class of item equipped to that equip point
String itemClassEquipped;
/**
* the equip point
* @return
*/
public String getEquipPoint(){
return equipPoint;
}
/**
* the class of item equipped to that equip point
* @return
*/
public String getItemClassEquipped(){
return itemClassEquipped;
}
}

View File

@ -17,6 +17,8 @@ public class EquipPoint {
List<Float> offsetVector; List<Float> offsetVector;
//the rotation to apply to the items that are attached to the bone //the rotation to apply to the items that are attached to the bone
List<Float> offsetRotation; List<Float> offsetRotation;
//signals that this equip point can block
boolean canBlock;
//the equip classes that are whitelisted for this equip point //the equip classes that are whitelisted for this equip point
List<String> equipClassWhitelist; List<String> equipClassWhitelist;
@ -68,6 +70,14 @@ public class EquipPoint {
this.offsetRotation = offsetRotation; this.offsetRotation = offsetRotation;
} }
/**
* Signals that this equip point can block
* @return true if can block, false otherwise
*/
public boolean getCanBlock(){
return canBlock;
}
/** /**
* Gets the equip classes that are whitelisted for this equip point * Gets the equip classes that are whitelisted for this equip point
* @return the classes * @return the classes

View File

@ -4,50 +4,97 @@ import electrosphere.game.data.collidable.CollidableTemplate;
import java.util.List; import java.util.List;
/**
* Data on a given item
*/
public class Item { public class Item {
//the id of the item
String itemId; String itemId;
//the model path of the item
String modelPath; String modelPath;
//tokens associated with this item type
List<String> tokens; List<String> tokens;
//the collidable data for the item
CollidableTemplate collidable; CollidableTemplate collidable;
//the equip whitelist for this item (what creatures can equip this item?)
List<EquipWhitelist> equipWhitelist; List<EquipWhitelist> equipWhitelist;
//the idle animation for the item
String idleAnim; String idleAnim;
//the path for the icon texture for this item
String iconPath; String iconPath;
//the class of item
String equipClass; String equipClass;
//weapon data for this item if it is an item
WeaponData weaponData; WeaponData weaponData;
/**
* the id of the item
* @return
*/
public String getItemId() { public String getItemId() {
return itemId; return itemId;
} }
/**
* the model path of the item
* @return
*/
public String getModelPath() { public String getModelPath() {
return modelPath; return modelPath;
} }
/*
* tokens associated with this item type
*/
public List<String> getTokens() { public List<String> getTokens() {
return tokens; return tokens;
} }
/**
* the collidable data for the item
* @return
*/
public CollidableTemplate getCollidable(){ public CollidableTemplate getCollidable(){
return collidable; return collidable;
} }
/**
* the equip whitelist for this item (what creatures can equip this item?)
* @return
*/
public List<EquipWhitelist> getEquipWhitelist(){ public List<EquipWhitelist> getEquipWhitelist(){
return equipWhitelist; return equipWhitelist;
} }
/**
* the idle animation for the item
* @return
*/
public String getIdleAnim(){ public String getIdleAnim(){
return idleAnim; return idleAnim;
} }
/**
* the path for the icon texture for this item
* @return
*/
public String getIconPath(){ public String getIconPath(){
return iconPath; return iconPath;
} }
/**
* the class of item
* @return
*/
public String getEquipClass(){ public String getEquipClass(){
return equipClass; return equipClass;
} }
/**
* weapon data for this item if it is an item
* @return
*/
public WeaponData getWeaponData(){ public WeaponData getWeaponData(){
return weaponData; return weaponData;
} }

View File

@ -1,14 +1,6 @@
package electrosphere.net.client.protocol; package electrosphere.net.client.protocol;
import electrosphere.client.scene.ClientWorldData;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.engine.Main;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.AuthMessage; import electrosphere.net.parser.net.message.AuthMessage;
import electrosphere.net.parser.net.message.CharacterMessage; import electrosphere.net.parser.net.message.CharacterMessage;
import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.net.parser.net.message.EntityMessage;
@ -19,8 +11,6 @@ import electrosphere.net.parser.net.message.PlayerMessage;
import electrosphere.net.parser.net.message.ServerMessage; import electrosphere.net.parser.net.message.ServerMessage;
import electrosphere.net.parser.net.message.SynchronizationMessage; import electrosphere.net.parser.net.message.SynchronizationMessage;
import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.parser.net.message.TerrainMessage;
import org.joml.Vector2f;
import org.joml.Vector3f;
public class ClientProtocol { public class ClientProtocol {

View File

@ -5,7 +5,6 @@ import electrosphere.entity.Entity;
import electrosphere.entity.state.equip.ClientEquipState; import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.entity.state.inventory.ClientInventoryState; import electrosphere.entity.state.inventory.ClientInventoryState;
import electrosphere.entity.state.inventory.InventoryUtils; import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.game.data.creature.type.equip.EquipPoint; import electrosphere.game.data.creature.type.equip.EquipPoint;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.InventoryMessage; import electrosphere.net.parser.net.message.InventoryMessage;
@ -70,6 +69,7 @@ public class InventoryProtocol {
} }
} }
break; break;
case CLIENTREQUESTPERFORMITEMACTION:
case CLIENTREQUESTUNEQUIPITEM: case CLIENTREQUESTUNEQUIPITEM:
case CLIENTREQUESTEQUIPITEM: case CLIENTREQUESTEQUIPITEM:
//silently ignore //silently ignore

View File

@ -15,6 +15,7 @@ public class InventoryMessage extends NetworkMessage {
SERVERCOMMANDEQUIPITEM, SERVERCOMMANDEQUIPITEM,
SERVERCOMMANDUNEQUIPITEM, SERVERCOMMANDUNEQUIPITEM,
CLIENTREQUESTUNEQUIPITEM, CLIENTREQUESTUNEQUIPITEM,
CLIENTREQUESTPERFORMITEMACTION,
} }
InventoryMessageType messageType; InventoryMessageType messageType;
@ -23,6 +24,8 @@ public class InventoryMessage extends NetworkMessage {
int entityId; int entityId;
int equipperId; int equipperId;
int containerType; int containerType;
int itemActionCode;
int itemActionCodeState;
InventoryMessage(InventoryMessageType messageType){ InventoryMessage(InventoryMessageType messageType){
this.type = MessageType.INVENTORY_MESSAGE; this.type = MessageType.INVENTORY_MESSAGE;
@ -73,6 +76,22 @@ public class InventoryMessage extends NetworkMessage {
this.containerType = containerType; this.containerType = containerType;
} }
public int getitemActionCode() {
return itemActionCode;
}
public void setitemActionCode(int itemActionCode) {
this.itemActionCode = itemActionCode;
}
public int getitemActionCodeState() {
return itemActionCodeState;
}
public void setitemActionCodeState(int itemActionCodeState) {
this.itemActionCodeState = itemActionCodeState;
}
static void stripPacketHeader(CircularByteBuffer byteBuffer){ static void stripPacketHeader(CircularByteBuffer byteBuffer){
byteBuffer.read(2); byteBuffer.read(2);
} }
@ -97,6 +116,8 @@ public class InventoryMessage extends NetworkMessage {
return InventoryMessage.canParseserverCommandUnequipItemMessage(byteBuffer); return InventoryMessage.canParseserverCommandUnequipItemMessage(byteBuffer);
case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTUNEQUIPITEM: case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTUNEQUIPITEM:
return InventoryMessage.canParseclientRequestUnequipItemMessage(byteBuffer); return InventoryMessage.canParseclientRequestUnequipItemMessage(byteBuffer);
case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION:
return InventoryMessage.canParseclientRequestPerformItemActionMessage(byteBuffer);
} }
return false; return false;
} }
@ -363,6 +384,49 @@ public class InventoryMessage extends NetworkMessage {
return rVal; return rVal;
} }
public static boolean canParseclientRequestPerformItemActionMessage(CircularByteBuffer byteBuffer){
int currentStreamLength = byteBuffer.getRemaining();
List<Byte> temporaryByteQueue = new LinkedList();
int equipPointIdSize = 0;
if(currentStreamLength < 6){
return false;
} else {
temporaryByteQueue.add(byteBuffer.peek(2 + 0));
temporaryByteQueue.add(byteBuffer.peek(2 + 1));
temporaryByteQueue.add(byteBuffer.peek(2 + 2));
temporaryByteQueue.add(byteBuffer.peek(2 + 3));
equipPointIdSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
}
if(currentStreamLength < 6 + equipPointIdSize){
return false;
}
if(currentStreamLength < 10 + equipPointIdSize){
return false;
}
if(currentStreamLength < 14 + equipPointIdSize){
return false;
}
return true;
}
public static InventoryMessage parseclientRequestPerformItemActionMessage(CircularByteBuffer byteBuffer){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTPERFORMITEMACTION);
stripPacketHeader(byteBuffer);
rVal.setequipPointId(ByteStreamUtils.popStringFromByteQueue(byteBuffer));
rVal.setitemActionCode(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setitemActionCodeState(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
return rVal;
}
public static InventoryMessage constructclientRequestPerformItemActionMessage(String equipPointId,int itemActionCode,int itemActionCodeState){
InventoryMessage rVal = new InventoryMessage(InventoryMessageType.CLIENTREQUESTPERFORMITEMACTION);
rVal.setequipPointId(equipPointId);
rVal.setitemActionCode(itemActionCode);
rVal.setitemActionCodeState(itemActionCodeState);
rVal.serialize();
return rVal;
}
@Override @Override
void serialize(){ void serialize(){
byte[] intValues = new byte[8]; byte[] intValues = new byte[8];
@ -505,6 +569,29 @@ public class InventoryMessage extends NetworkMessage {
rawBytes[6+i] = stringBytes[i]; rawBytes[6+i] = stringBytes[i];
} }
break; break;
case CLIENTREQUESTPERFORMITEMACTION:
rawBytes = new byte[2+4+equipPointId.length()+4+4];
//message header
rawBytes[0] = TypeBytes.MESSAGE_TYPE_INVENTORY;
//entity messaage header
rawBytes[1] = TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION;
intValues = ByteStreamUtils.serializeIntToBytes(equipPointId.length());
for(int i = 0; i < 4; i++){
rawBytes[2+i] = intValues[i];
}
stringBytes = equipPointId.getBytes();
for(int i = 0; i < equipPointId.length(); i++){
rawBytes[6+i] = stringBytes[i];
}
intValues = ByteStreamUtils.serializeIntToBytes(itemActionCode);
for(int i = 0; i < 4; i++){
rawBytes[6+equipPointId.length()+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeIntToBytes(itemActionCodeState);
for(int i = 0; i < 4; i++){
rawBytes[10+equipPointId.length()+i] = intValues[i];
}
break;
} }
serialized = true; serialized = true;
} }

View File

@ -311,6 +311,11 @@ SYNCHRONIZATION_MESSAGE,
rVal = InventoryMessage.parseclientRequestUnequipItemMessage(byteBuffer); rVal = InventoryMessage.parseclientRequestUnequipItemMessage(byteBuffer);
} }
break; break;
case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION:
if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
rVal = InventoryMessage.parseclientRequestPerformItemActionMessage(byteBuffer);
}
break;
} }
break; break;
case TypeBytes.MESSAGE_TYPE_SYNCHRONIZATION: case TypeBytes.MESSAGE_TYPE_SYNCHRONIZATION:

View File

@ -133,6 +133,7 @@ Message categories
public static final byte INVENTORY_MESSAGE_TYPE_SERVERCOMMANDEQUIPITEM = 4; public static final byte INVENTORY_MESSAGE_TYPE_SERVERCOMMANDEQUIPITEM = 4;
public static final byte INVENTORY_MESSAGE_TYPE_SERVERCOMMANDUNEQUIPITEM = 5; public static final byte INVENTORY_MESSAGE_TYPE_SERVERCOMMANDUNEQUIPITEM = 5;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTUNEQUIPITEM = 6; public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTUNEQUIPITEM = 6;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION = 7;
/* /*
Inventory packet sizes Inventory packet sizes
*/ */

View File

@ -5,41 +5,45 @@ import electrosphere.engine.Main;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.net.NetUtils; import electrosphere.net.NetUtils;
import electrosphere.net.parser.net.message.NetworkMessage; import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.server.saves.SaveUtils;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.BindException; import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/** /**
* * Lowest level networking class for the server
* @author amaterasu
*/ */
public class Server implements Runnable{ public class Server implements Runnable{
//the port the server is running on
int port; int port;
//the socket for the server
ServerSocket serverSocket; ServerSocket serverSocket;
//the map of ip->connection handler
Map<String,ServerConnectionHandler> clientMap = new HashMap<String,ServerConnectionHandler>(); Map<String,ServerConnectionHandler> clientMap = new HashMap<String,ServerConnectionHandler>();
/**
* Inits the server
*/
void initServer(){ void initServer(){
// clientMap = new HashMap<String,ServerConnectionHandler>(); // clientMap = new HashMap<String,ServerConnectionHandler>();
} }
/**
* Constructor
* @param port The port to run the server on
*/
public Server(int port){ public Server(int port){
this.port = port; this.port = port;
} }
@ -76,7 +80,19 @@ public class Server implements Runnable{
} }
} }
} }
/**
* Synchronously handles queued packets for each client connection
*/
public void synchronousPacketHandling(){
for(ServerConnectionHandler connectionHandler : this.clientMap.values()){
connectionHandler.handleSynchronousPacketQueue();
}
}
/**
* Closes the server socket
*/
public void close(){ public void close(){
try { try {
if(serverSocket != null){ if(serverSocket != null){
@ -87,6 +103,10 @@ public class Server implements Runnable{
} }
} }
/**
* Broadcasts a message to all clients
* @param message The message to broadcast
*/
public void broadcastMessage(NetworkMessage message){ public void broadcastMessage(NetworkMessage message){
for(ServerConnectionHandler client : clientMap.values()){ for(ServerConnectionHandler client : clientMap.values()){
if(Globals.clientPlayer == null || client.playerID != Globals.clientPlayer.getId()){ if(Globals.clientPlayer == null || client.playerID != Globals.clientPlayer.getId()){
@ -95,6 +115,12 @@ public class Server implements Runnable{
} }
} }
/**
* Adds a connection created manually by two streams instead of receiving the streams from the server socket
* @param serverInputStream The input stream
* @param serverOutputStream The output stream
* @return The connection object for the provided streams
*/
public ServerConnectionHandler addLocalPlayer(InputStream serverInputStream, OutputStream serverOutputStream){ public ServerConnectionHandler addLocalPlayer(InputStream serverInputStream, OutputStream serverOutputStream){
ServerConnectionHandler newClient = new ServerConnectionHandler(serverInputStream,serverOutputStream); ServerConnectionHandler newClient = new ServerConnectionHandler(serverInputStream,serverOutputStream);
clientMap.put("127.0.0.1", newClient); clientMap.put("127.0.0.1", newClient);

View File

@ -1,21 +1,12 @@
package electrosphere.net.server; package electrosphere.net.server;
import electrosphere.entity.types.creature.CreatureTemplate; import electrosphere.entity.types.creature.CreatureTemplate;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.engine.Main; import electrosphere.engine.Main;
import electrosphere.entity.Entity; import electrosphere.entity.ServerEntityUtils;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.ironsight.IronSightTree;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.entity.types.collision.CollisionObjUtils;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.net.NetUtils;
import electrosphere.net.parser.net.message.AuthMessage; import electrosphere.net.parser.net.message.AuthMessage;
import electrosphere.net.parser.net.message.EntityMessage;
import electrosphere.net.parser.net.message.NetworkMessage; import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.net.parser.net.message.PlayerMessage;
import electrosphere.net.parser.net.message.ServerMessage; import electrosphere.net.parser.net.message.ServerMessage;
import electrosphere.net.parser.net.raw.NetworkParser; import electrosphere.net.parser.net.raw.NetworkParser;
import electrosphere.net.server.player.Player; import electrosphere.net.server.player.Player;
@ -24,30 +15,20 @@ import electrosphere.net.server.protocol.ServerProtocol;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.math.BigInteger;
import java.net.Socket; import java.net.Socket;
import java.net.SocketException; import java.net.SocketException;
import java.nio.ByteBuffer;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.spec.SecretKeySpec;
import org.joml.Vector3d;
import org.joml.Vector3f;
/** /**
* * A connection to the server
* @author satellite
*/ */
public class ServerConnectionHandler implements Runnable { public class ServerConnectionHandler implements Runnable {
//the player id associated with this connection
static int playerIdIncrementer = 0; static int playerIdIncrementer = 0;
//local carrier variables //local carrier variables
@ -55,17 +36,30 @@ public class ServerConnectionHandler implements Runnable {
//socket carrier variables //socket carrier variables
Socket socket; Socket socket;
//the streams for the connection
// CryptoInputStream inputStream; // CryptoInputStream inputStream;
// CryptoOutputStream outputStream; // CryptoOutputStream outputStream;
InputStream inputStream; InputStream inputStream;
OutputStream outputStream; OutputStream outputStream;
boolean initialized;
boolean isAuthenticated = false; //the network parser for the streams
NetworkParser networkParser; NetworkParser networkParser;
//initialized status
boolean initialized;
//authentication status
boolean isAuthenticated = false;
//the player id
int playerID; int playerID;
int playerCharacterID; //the player's entity id
int playerEntityID;
//the creature template associated with this player
CreatureTemplate currentCreatureTemplate; CreatureTemplate currentCreatureTemplate;
//the server protocol object associated with this player
ServerProtocol serverProtocol; ServerProtocol serverProtocol;
//thresholds for determining when to send pings and when a client has disconnected //thresholds for determining when to send pings and when a client has disconnected
@ -82,13 +76,27 @@ public class ServerConnectionHandler implements Runnable {
String netMonitorHandle; String netMonitorHandle;
//Used to copy messages from network parser to NetMonitor //Used to copy messages from network parser to NetMonitor
List<NetworkMessage> netMonitorCache = new LinkedList<NetworkMessage>(); List<NetworkMessage> netMonitorCache = new LinkedList<NetworkMessage>();
//the lock used for synchronizing the synchronous message queue
Semaphore synchronousMessageLock = new Semaphore(1);
//the queue of synchonous network messages
List<NetworkMessage> synchronousMessageQueue = new CopyOnWriteArrayList<NetworkMessage>();
/**
* Constructs a connection from a socket
* @param socket the socket
*/
public ServerConnectionHandler(Socket socket) { public ServerConnectionHandler(Socket socket) {
this.socket = socket; this.socket = socket;
playerID = getNewPlayerID(); playerID = getNewPlayerID();
LoggerInterface.loggerNetworking.INFO("Player ID: " + playerID); LoggerInterface.loggerNetworking.INFO("Player ID: " + playerID);
} }
/**
* Constructs a connection from an arbitrary input and output stream
* @param serverInputStream the input stream
* @param serverOutputStream the output stream
*/
public ServerConnectionHandler(InputStream serverInputStream, OutputStream serverOutputStream){ public ServerConnectionHandler(InputStream serverInputStream, OutputStream serverOutputStream){
this.local = true; this.local = true;
playerID = getNewPlayerID(); playerID = getNewPlayerID();
@ -114,6 +122,8 @@ public class ServerConnectionHandler implements Runnable {
} catch (SocketException ex) { } catch (SocketException ex) {
ex.printStackTrace(); ex.printStackTrace();
} }
//TODO: use this commented block of code as a reference for implementing encryption on top of the game connection
// final SecretKeySpec key = new SecretKeySpec(("1234567890123456").getBytes(),"AES"); // final SecretKeySpec key = new SecretKeySpec(("1234567890123456").getBytes(),"AES");
// final Properties properties = new Properties(); // final Properties properties = new Properties();
// final RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(4096, BigInteger.probablePrime(4000, new Random())); // final RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(4096, BigInteger.probablePrime(4000, new Random()));
@ -210,7 +220,13 @@ public class ServerConnectionHandler implements Runnable {
//ponder incoming messages //ponder incoming messages
while(networkParser.hasIncomingMessaage()){ while(networkParser.hasIncomingMessaage()){
NetworkMessage message = networkParser.popIncomingMessage(); NetworkMessage message = networkParser.popIncomingMessage();
serverProtocol.handleMessage(message); NetworkMessage bouncedMessage = serverProtocol.handleAsyncMessage(message);
//if the message bounces back from the async handle function, means we need to handle synchronously
if(bouncedMessage != null){
this.synchronousMessageLock.acquireUninterruptibly();
this.synchronousMessageQueue.add(bouncedMessage);
this.synchronousMessageLock.release();
}
} }
if(Globals.netMonitor != null){ if(Globals.netMonitor != null){
@ -231,6 +247,18 @@ public class ServerConnectionHandler implements Runnable {
ex.printStackTrace(); ex.printStackTrace();
} }
} }
/**
* Handles synchronous packets in the queue
*/
public void handleSynchronousPacketQueue(){
synchronousMessageLock.acquireUninterruptibly();
for(NetworkMessage message : synchronousMessageQueue){
this.serverProtocol.handleSynchronousMessage(message);
}
synchronousMessageQueue.clear();
synchronousMessageLock.release();
}
public void setPlayerId(int id){ public void setPlayerId(int id){
playerID = id; playerID = id;
@ -245,12 +273,12 @@ public class ServerConnectionHandler implements Runnable {
return playerIdIncrementer; return playerIdIncrementer;
} }
public void setPlayerCharacterId(int id){ public void setPlayerEntityId(int id){
playerCharacterID = id; playerEntityID = id;
} }
public int getPlayerCharacterId(){ public int getPlayerEntityId(){
return playerCharacterID; return playerEntityID;
} }
public String getIPAddress(){ public String getIPAddress(){
@ -266,7 +294,7 @@ public class ServerConnectionHandler implements Runnable {
} }
boolean isConnectionPlayerEntity(int id){ boolean isConnectionPlayerEntity(int id){
return id == this.playerCharacterID; return id == this.playerEntityID;
} }
/** /**
@ -303,13 +331,8 @@ public class ServerConnectionHandler implements Runnable {
} }
//figure out what player we are //figure out what player we are
Player playerObject = Globals.playerManager.getPlayerFromId(getPlayerId()); Player playerObject = Globals.playerManager.getPlayerFromId(getPlayerId());
//get player entity & position //tell all clients to destroy the entity
Entity playerEntity = playerObject.getPlayerEntity(); ServerEntityUtils.destroyEntity(playerObject.getPlayerEntity());
Vector3d position = EntityUtils.getPosition(playerEntity);
//deregister entity
EntityUtils.cleanUpEntity(playerObject.getPlayerEntity());
//TODO: tell all clients to destroy the entity
EntityUtils.cleanUpEntity(playerEntity);
} }
} }

View File

@ -39,4 +39,20 @@ public class AuthProtocol {
} }
} }
/**
* Handles the network message from the context of the main server simulation thread
* @param connectionHandler The connection handler
* @param message The network message
*/
protected static void handleSynchronousAuthMessage(ServerConnectionHandler connectionHandler, AuthMessage message){
switch(message.getMessageSubtype()){
case AUTHDETAILS:
case AUTHFAILURE:
case AUTHREQUEST:
case AUTHSUCCESS:
//silently ignore
break;
}
}
} }

View File

@ -37,6 +37,25 @@ public class CharacterProtocol {
} }
} }
/**
* Handles the network message from the context of the main server simulation thread
* @param connectionHandler The connection handler
* @param message The network message
*/
protected static void handleSynchronousCharacterMessage(ServerConnectionHandler connectionHandler, CharacterMessage message){
switch(message.getMessageSubtype()){
case REQUESTCHARACTERLIST:
case REQUESTCREATECHARACTER:
case REQUESTSPAWNCHARACTER:
case RESPONSECHARACTERLIST:
case RESPONSECREATECHARACTERFAILURE:
case RESPONSECREATECHARACTERSUCCESS:
case RESPONSESPAWNCHARACTER:
//silently ignore
break;
}
}
/** /**
* Spawns the player's entity * Spawns the player's entity
* @param connectionHandler The connection handler for the player * @param connectionHandler The connection handler for the player

View File

@ -52,5 +52,29 @@ public class EntityProtocol {
break; break;
} }
} }
/**
* Handles the network message from the context of the main server simulation thread
* @param connectionHandler The connection handler
* @param message The network message
*/
protected static void handleSynchronousEntityMessage(ServerConnectionHandler connectionHandler, EntityMessage message){
switch(message.getMessageSubtype()){
case MOVEUPDATE:
case ATTACKUPDATE:
case STARTATTACK:
case UPDATEENTITYVIEWDIR:
case KILL:
case SPAWNCREATURE:
case DESTROY:
case CREATE:
case ATTACHENTITYTOENTITY:
case SETPROPERTY:
case SPAWNFOLIAGESEED:
case SPAWNITEM:
//silently ignore
break;
}
}
} }

View File

@ -1,54 +1,88 @@
package electrosphere.net.server.protocol; package electrosphere.net.server.protocol;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity; import electrosphere.entity.Entity;
import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.entity.state.inventory.InventoryUtils; import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.game.data.creature.type.equip.EquipPoint;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.net.parser.net.message.InventoryMessage; import electrosphere.net.parser.net.message.InventoryMessage;
import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.server.datacell.utils.EntityLookupUtils; import electrosphere.server.datacell.utils.EntityLookupUtils;
import electrosphere.server.player.PlayerActions;
/**
* Server protocol for dealing with inventory messages
*/
public class InventoryProtocol { public class InventoryProtocol {
//the entity's equip inventory
public static final int INVENTORY_TYPE_EQUIP = 0; public static final int INVENTORY_TYPE_EQUIP = 0;
//the natural inventory of the entity
public static final int INVENTORY_TYPE_NATURAL = 1; public static final int INVENTORY_TYPE_NATURAL = 1;
protected static void handleInventoryMessage(ServerConnectionHandler connectionHandler, InventoryMessage message){ /**
Entity characterEntity; * Handles asynchronous inventory messages
* @param connectionHandler The connection handler
* @param message The message to handle
* @return The network message if it should be handled synchronously, otherwise null
*/
protected static InventoryMessage handleAsyncInventoryMessage(ServerConnectionHandler connectionHandler, InventoryMessage message){
Entity target; Entity target;
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case ADDITEMTOINVENTORY: case ADDITEMTOINVENTORY:
LoggerInterface.loggerNetworking.DEBUG("[SERVER] ADD ITEM TO INVENTORY " + message.getentityId()); LoggerInterface.loggerNetworking.DEBUG("[SERVER] ADD ITEM TO INVENTORY " + message.getentityId());
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerCharacterId()); target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
if(InventoryUtils.hasNaturalInventory(target)){ if(InventoryUtils.hasNaturalInventory(target)){
InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message); InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
} }
break; break;
case REMOVEITEMFROMINVENTORY: case REMOVEITEMFROMINVENTORY:
LoggerInterface.loggerNetworking.DEBUG("[SERVER] REMOVE ITEM FROM INVENTORY " + message.getentityId()); LoggerInterface.loggerNetworking.DEBUG("[SERVER] REMOVE ITEM FROM INVENTORY " + message.getentityId());
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerCharacterId()); target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
if(InventoryUtils.hasNaturalInventory(target)){ if(InventoryUtils.hasNaturalInventory(target)){
InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message); InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
} }
break; break;
case CLIENTREQUESTEQUIPITEM: case CLIENTREQUESTEQUIPITEM:
LoggerInterface.loggerNetworking.DEBUG("[SERVER] REQUEST EQUIP ITEM " + message.getentityId()); LoggerInterface.loggerNetworking.DEBUG("[SERVER] REQUEST EQUIP ITEM " + message.getentityId());
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerCharacterId()); target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
if(InventoryUtils.hasNaturalInventory(target)){ if(InventoryUtils.hasNaturalInventory(target)){
InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message); InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
} }
break; break;
case CLIENTREQUESTUNEQUIPITEM: case CLIENTREQUESTUNEQUIPITEM:
LoggerInterface.loggerNetworking.DEBUG("[SERVER] REQUEST UNEQUIP ITEM " + message.getentityId()); LoggerInterface.loggerNetworking.DEBUG("[SERVER] REQUEST UNEQUIP ITEM " + message.getentityId());
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerCharacterId()); target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
if(InventoryUtils.hasNaturalInventory(target)){ if(InventoryUtils.hasNaturalInventory(target)){
InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message); InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
} }
break; break;
case CLIENTREQUESTPERFORMITEMACTION: {
//perform some action on the server based on what the client has equipped
return message;
}
case SERVERCOMMANDUNEQUIPITEM:
case SERVERCOMMANDMOVEITEMCONTAINER:
case SERVERCOMMANDEQUIPITEM:
//silently ignore
break;
}
return null;
}
/**
* Handles the network message from the context of the main server simulation thread
* @param connectionHandler The connection handler
* @param message The network message
*/
protected static void handleSynchronousInventoryMessage(ServerConnectionHandler connectionHandler, InventoryMessage message){
switch(message.getMessageSubtype()){
case CLIENTREQUESTPERFORMITEMACTION: {
PlayerActions.attemptPlayerAction(connectionHandler, message);
} break;
case ADDITEMTOINVENTORY:
case REMOVEITEMFROMINVENTORY:
case CLIENTREQUESTEQUIPITEM:
case CLIENTREQUESTUNEQUIPITEM:
case SERVERCOMMANDUNEQUIPITEM: case SERVERCOMMANDUNEQUIPITEM:
case SERVERCOMMANDMOVEITEMCONTAINER: case SERVERCOMMANDMOVEITEMCONTAINER:
case SERVERCOMMANDEQUIPITEM: case SERVERCOMMANDEQUIPITEM:

View File

@ -22,5 +22,19 @@ public class LoreProtocol {
break; break;
} }
} }
/**
* Handles the network message from the context of the main server simulation thread
* @param connectionHandler The connection handler
* @param message The network message
*/
protected static void handleSynchronousLoreMessage(ServerConnectionHandler connectionHandler, LoreMessage message){
switch(message.getMessageSubtype()){
case REQUESTRACES:
case RESPONSERACES:
//silently ignore
break;
}
}
} }

View File

@ -3,11 +3,36 @@ package electrosphere.net.server.protocol;
import electrosphere.net.parser.net.message.PlayerMessage; import electrosphere.net.parser.net.message.PlayerMessage;
import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.ServerConnectionHandler;
/**
* Player protocol handling
*/
public class PlayerProtocol { public class PlayerProtocol {
/**
* Handles a player network message
* @param connectionHandler The connection handler
* @param message The player message
*/
protected static void handlePlayerMessage(ServerConnectionHandler connectionHandler, PlayerMessage message){ protected static void handlePlayerMessage(ServerConnectionHandler connectionHandler, PlayerMessage message){
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case SETINITIALDISCRETEPOSITION:
case SET_ID:
//silently ignore
break;
}
}
/**
* Handles the network message from the context of the main server simulation thread
* @param connectionHandler The connection handler
* @param message The network message
*/
protected static void handleSynchronousPlayerMessage(ServerConnectionHandler connectionHandler, PlayerMessage message){
switch(message.getMessageSubtype()){
case SETINITIALDISCRETEPOSITION:
case SET_ID:
//silently ignore
break;
} }
} }

View File

@ -1,18 +1,6 @@
package electrosphere.net.server.protocol; package electrosphere.net.server.protocol;
import org.joml.Vector3f;
import electrosphere.engine.Globals;
import electrosphere.engine.Main;
import electrosphere.entity.Entity;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.state.ironsight.IronSightTree;
import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.entity.types.collision.CollisionObjUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.entity.types.item.ItemUtils;
import electrosphere.logger.LoggerInterface; import electrosphere.logger.LoggerInterface;
import electrosphere.net.NetUtils;
import electrosphere.net.parser.net.message.AuthMessage; import electrosphere.net.parser.net.message.AuthMessage;
import electrosphere.net.parser.net.message.CharacterMessage; import electrosphere.net.parser.net.message.CharacterMessage;
import electrosphere.net.parser.net.message.EntityMessage; import electrosphere.net.parser.net.message.EntityMessage;
@ -25,53 +13,110 @@ import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.net.parser.net.message.NetworkMessage.MessageType; import electrosphere.net.parser.net.message.NetworkMessage.MessageType;
import electrosphere.net.parser.net.message.ServerMessage.ServerMessageType; import electrosphere.net.parser.net.message.ServerMessage.ServerMessageType;
import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.net.server.player.Player;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.server.terrain.models.TerrainModification;
/**
* The server's protocol for handling a message
*/
public class ServerProtocol { public class ServerProtocol {
//the connection handler associated with this protocol object
ServerConnectionHandler connectionHandler; ServerConnectionHandler connectionHandler;
//if set to true, will log ping messages
boolean echoPings = false; boolean echoPings = false;
/**
* Constructor
* @param connectionHandler the associated connection handler
*/
public ServerProtocol(ServerConnectionHandler connectionHandler){ public ServerProtocol(ServerConnectionHandler connectionHandler){
this.connectionHandler = connectionHandler; this.connectionHandler = connectionHandler;
} }
public void handleMessage(NetworkMessage message){ /**
* Handles a given network message
* @param message The network message to handle
* @return Returns the message if it should be synchronously handled with the main server thread instead
*/
public NetworkMessage handleAsyncMessage(NetworkMessage message){
//print out message //print out message
printMessage(message); printMessage(message);
//actually handle message //actually handle message
switch(message.getType()){ switch(message.getType()){
case ENTITY_MESSAGE: case ENTITY_MESSAGE: {
EntityProtocol.handleEntityMessage(connectionHandler, (EntityMessage)message); EntityProtocol.handleEntityMessage(connectionHandler, (EntityMessage)message);
break; } break;
case TERRAIN_MESSAGE: case TERRAIN_MESSAGE: {
TerrainProtocol.handleTerrainMessage(connectionHandler, (TerrainMessage)message); TerrainProtocol.handleTerrainMessage(connectionHandler, (TerrainMessage)message);
break; } break;
case PLAYER_MESSAGE: case PLAYER_MESSAGE: {
PlayerProtocol.handlePlayerMessage(connectionHandler, (PlayerMessage)message); PlayerProtocol.handlePlayerMessage(connectionHandler, (PlayerMessage)message);
break; } break;
case AUTH_MESSAGE: case AUTH_MESSAGE: {
AuthProtocol.handleAuthenticationMessage(connectionHandler, (AuthMessage)message); AuthProtocol.handleAuthenticationMessage(connectionHandler, (AuthMessage)message);
break; } break;
case SERVER_MESSAGE: case SERVER_MESSAGE: {
handleServerMessage((ServerMessage)message); handleServerMessage((ServerMessage)message);
break; } break;
case CHARACTER_MESSAGE: case CHARACTER_MESSAGE: {
CharacterProtocol.handleCharacterMessage(connectionHandler, (CharacterMessage)message); CharacterProtocol.handleCharacterMessage(connectionHandler, (CharacterMessage)message);
break; } break;
case LORE_MESSAGE: case LORE_MESSAGE: {
LoreProtocol.handleLoreMessage(connectionHandler, (LoreMessage)message); LoreProtocol.handleLoreMessage(connectionHandler, (LoreMessage)message);
} break;
case INVENTORY_MESSAGE: {
return InventoryProtocol.handleAsyncInventoryMessage(connectionHandler, (InventoryMessage)message);
}
case SYNCHRONIZATION_MESSAGE:
//silently ignore sync messages from client
break; break;
case INVENTORY_MESSAGE: }
InventoryProtocol.handleInventoryMessage(connectionHandler, (InventoryMessage)message); return null;
}
/**
* Synchronously handles a network message
* @param message The network message
*/
public void handleSynchronousMessage(NetworkMessage message){
//print out message
printMessage(message);
//actually handle message
switch(message.getType()){
case ENTITY_MESSAGE: {
EntityProtocol.handleSynchronousEntityMessage(connectionHandler, (EntityMessage)message);
} break;
case TERRAIN_MESSAGE: {
TerrainProtocol.handleSynchronousServerMessage(connectionHandler, (TerrainMessage)message);
} break;
case PLAYER_MESSAGE: {
PlayerProtocol.handleSynchronousPlayerMessage(connectionHandler, (PlayerMessage)message);
} break;
case AUTH_MESSAGE: {
AuthProtocol.handleSynchronousAuthMessage(connectionHandler, (AuthMessage)message);
} break;
case SERVER_MESSAGE: {
ServerProtocol.handleSynchronousServerMessage(connectionHandler, (ServerMessage)message);
} break;
case CHARACTER_MESSAGE: {
CharacterProtocol.handleSynchronousCharacterMessage(connectionHandler, (CharacterMessage)message);
} break;
case LORE_MESSAGE: {
LoreProtocol.handleSynchronousLoreMessage(connectionHandler, (LoreMessage)message);
} break;
case INVENTORY_MESSAGE: {
InventoryProtocol.handleSynchronousInventoryMessage(connectionHandler, (InventoryMessage)message);
} break;
case SYNCHRONIZATION_MESSAGE:
//silently ignore sync messages from client
break; break;
} }
} }
/**
* Handles a server-type network message
* @param message The server message
*/
void handleServerMessage(ServerMessage message){ void handleServerMessage(ServerMessage message){
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case PING: case PING:
@ -83,6 +128,20 @@ public class ServerProtocol {
} }
} }
/**
* Handles the network message from the context of the main server simulation thread
* @param connectionHandler The connection handler
* @param message The network message
*/
protected static void handleSynchronousServerMessage(ServerConnectionHandler connectionHandler, ServerMessage message){
switch(message.getMessageSubtype()){
case PING:
case PONG:
//silently ignore
break;
}
}
/** /**
* Print out the network message type, this only prints ping and pong if echoPings is true * Print out the network message type, this only prints ping and pong if echoPings is true
*/ */

View File

@ -6,20 +6,25 @@ import java.nio.IntBuffer;
import org.joml.Vector3d; import org.joml.Vector3d;
import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.engine.Globals; import electrosphere.engine.Globals;
import electrosphere.net.parser.net.message.TerrainMessage; import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.net.server.Server;
import electrosphere.net.server.ServerConnectionHandler; import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.net.server.player.Player; import electrosphere.net.server.player.Player;
import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.Realm;
import electrosphere.server.fluid.manager.ServerFluidChunk; import electrosphere.server.fluid.manager.ServerFluidChunk;
import electrosphere.server.terrain.editing.TerrainEditing; import electrosphere.server.terrain.editing.TerrainEditing;
import electrosphere.server.terrain.manager.ServerTerrainChunk; import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.server.terrain.models.TerrainModification;
/**
* Server handling for terrain network messages
*/
public class TerrainProtocol { public class TerrainProtocol {
/**
* Handles a terrain message
* @param connectionHandler The connection handler
* @param message The terrain message
*/
protected static void handleTerrainMessage(ServerConnectionHandler connectionHandler, TerrainMessage message){ protected static void handleTerrainMessage(ServerConnectionHandler connectionHandler, TerrainMessage message){
switch(message.getMessageSubtype()){ switch(message.getMessageSubtype()){
case REQUESTMETADATA: case REQUESTMETADATA:
@ -53,6 +58,36 @@ public class TerrainProtocol {
} }
} }
/**
* Handles the network message from the context of the main server simulation thread
* @param connectionHandler The connection handler
* @param message The network message
*/
protected static void handleSynchronousServerMessage(ServerConnectionHandler connectionHandler, TerrainMessage message){
switch(message.getMessageSubtype()){
case REQUESTCHUNKDATA:
case REQUESTEDITVOXEL:
case REQUESTFLUIDDATA:
case REQUESTMETADATA:
case REQUESTUSETERRAINPALETTE:
case RESPONSEMETADATA:
case UPDATEFLUIDDATA:
case SPAWNPOSITION:
case UPDATEVOXEL:
case SENDCHUNKDATA:
case SENDFLUIDDATA:
//silently ignore
break;
}
}
/**
* Sends a subchunk to the client
* @param connectionHandler The connection handler
* @param worldX the world x
* @param worldY the world y
* @param worldZ the world z
*/
static void sendWorldSubChunk(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ){ static void sendWorldSubChunk(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ){
/* /*
int locationX, int locationX,
@ -200,6 +235,13 @@ public class TerrainProtocol {
} }
/**
* Sends a fluid sub chunk to the client
* @param connectionHandler The connection handler
* @param worldX the world x
* @param worldY the world y
* @param worldZ the world z
*/
static void sendWorldFluidSubChunk(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ){ static void sendWorldFluidSubChunk(ServerConnectionHandler connectionHandler, int worldX, int worldY, int worldZ){
// System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY()); // System.out.println("Received request for chunk " + message.getworldX() + " " + message.getworldY());
@ -213,6 +255,10 @@ public class TerrainProtocol {
} }
/**
* Sends world metadata to the client
* @param connectionHandler The connection handler
*/
static void sendWorldMetadata(ServerConnectionHandler connectionHandler){ static void sendWorldMetadata(ServerConnectionHandler connectionHandler){
//world metadata //world metadata
connectionHandler.addMessagetoOutgoingQueue( connectionHandler.addMessagetoOutgoingQueue(
@ -233,7 +279,8 @@ public class TerrainProtocol {
* @param message The message containing the edit request * @param message The message containing the edit request
*/ */
static void attemptTerrainEdit(ServerConnectionHandler connectionHandler, TerrainMessage message){ static void attemptTerrainEdit(ServerConnectionHandler connectionHandler, TerrainMessage message){
Player player = Globals.playerManager.getPlayerFromId(connectionHandler.getPlayerId()); // Player player = Globals.playerManager.getPlayerFromId(connectionHandler.getPlayerId());
throw new UnsupportedOperationException();
} }
/** /**

View File

@ -7,13 +7,15 @@ public class BehaviorTreeIdEnums {
public static final int BTREE_CLIENTATTACKTREE_ID = 0; public static final int BTREE_CLIENTATTACKTREE_ID = 0;
public static final int BTREE_SERVERATTACKTREE_ID = 1; public static final int BTREE_SERVERATTACKTREE_ID = 1;
public static final int BTREE_CLIENTEQUIPSTATE_ID = 2; public static final int BTREE_CLIENTBLOCKTREE_ID = 2;
public static final int BTREE_SERVEREQUIPSTATE_ID = 3; public static final int BTREE_SERVERBLOCKTREE_ID = 3;
public static final int BTREE_GRAVITY_ID = 4; public static final int BTREE_CLIENTEQUIPSTATE_ID = 4;
public static final int BTREE_SERVERGRAVITY_ID = 5; public static final int BTREE_SERVEREQUIPSTATE_ID = 5;
public static final int BTREE_IDLE_ID = 6; public static final int BTREE_GRAVITY_ID = 6;
public static final int BTREE_SERVERIDLE_ID = 7; public static final int BTREE_SERVERGRAVITY_ID = 7;
public static final int BTREE_CLIENTGROUNDMOVEMENTTREE_ID = 8; public static final int BTREE_IDLE_ID = 8;
public static final int BTREE_SERVERGROUNDMOVEMENTTREE_ID = 9; public static final int BTREE_SERVERIDLE_ID = 9;
public static final int BTREE_CLIENTGROUNDMOVEMENTTREE_ID = 10;
public static final int BTREE_SERVERGROUNDMOVEMENTTREE_ID = 11;
} }

View File

@ -1,6 +1,10 @@
package electrosphere.net.synchronization; package electrosphere.net.synchronization;
import electrosphere.entity.state.block.ClientBlockTree;
import electrosphere.entity.state.block.ServerBlockTree;
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree; import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree; import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree;
@ -110,13 +114,25 @@ public class ClientSynchronizationManager {
} break; } break;
} }
} break; } break;
case BehaviorTreeIdEnums.BTREE_SERVERBLOCKTREE_ID: {
switch(message.getfieldId()){
case 8:{
ClientBlockTree tree = ClientBlockTree.getClientBlockTree(entity);
tree.setState(ClientBlockTree.getBlockStateShortAsEnum((short)message.getbTreeValue()));
} break;
case 9:{
ClientBlockTree tree = ClientBlockTree.getClientBlockTree(entity);
tree.setCurrentBlockVariant(message.getstringValue());
} break;
}
} break;
case BehaviorTreeIdEnums.BTREE_SERVEREQUIPSTATE_ID: { case BehaviorTreeIdEnums.BTREE_SERVEREQUIPSTATE_ID: {
switch(message.getfieldId()){ switch(message.getfieldId()){
} }
} break; } break;
case BehaviorTreeIdEnums.BTREE_SERVERGRAVITY_ID: { case BehaviorTreeIdEnums.BTREE_SERVERGRAVITY_ID: {
switch(message.getfieldId()){ switch(message.getfieldId()){
case 7:{ case 11:{
ClientGravityTree tree = ClientGravityTree.getClientGravityTree(entity); ClientGravityTree tree = ClientGravityTree.getClientGravityTree(entity);
tree.setState(ClientGravityTree.getGravityTreeStateShortAsEnum((short)message.getbTreeValue())); tree.setState(ClientGravityTree.getGravityTreeStateShortAsEnum((short)message.getbTreeValue()));
} break; } break;
@ -124,15 +140,15 @@ public class ClientSynchronizationManager {
} break; } break;
case BehaviorTreeIdEnums.BTREE_SERVERIDLE_ID: { case BehaviorTreeIdEnums.BTREE_SERVERIDLE_ID: {
switch(message.getfieldId()){ switch(message.getfieldId()){
case 9:{ case 13:{
ClientIdleTree tree = ClientIdleTree.getIdleTree(entity); ClientIdleTree tree = ClientIdleTree.getClientIdleTree(entity);
tree.setState(ClientIdleTree.getIdleTreeStateShortAsEnum((short)message.getbTreeValue())); tree.setState(ClientIdleTree.getIdleTreeStateShortAsEnum((short)message.getbTreeValue()));
} break; } break;
} }
} break; } break;
case BehaviorTreeIdEnums.BTREE_SERVERGROUNDMOVEMENTTREE_ID: { case BehaviorTreeIdEnums.BTREE_SERVERGROUNDMOVEMENTTREE_ID: {
switch(message.getfieldId()){ switch(message.getfieldId()){
case 11:{ case 15:{
ClientGroundMovementTree tree = ClientGroundMovementTree.getClientGroundMovementTree(entity); ClientGroundMovementTree tree = ClientGroundMovementTree.getClientGroundMovementTree(entity);
tree.setFacing(ClientGroundMovementTree.getMovementRelativeFacingShortAsEnum((short)message.getbTreeValue())); tree.setFacing(ClientGroundMovementTree.getMovementRelativeFacingShortAsEnum((short)message.getbTreeValue()));
} break; } break;

View File

@ -116,7 +116,12 @@ public class DebugContentPipeline implements RenderPipeline {
modelTransformMatrix.translate(cameraModifiedPosition); modelTransformMatrix.translate(cameraModifiedPosition);
//since you're directly accessing the quat from the body, need to adjust it to be in the correct orientation //since you're directly accessing the quat from the body, need to adjust it to be in the correct orientation
modelTransformMatrix.rotate(PhysicsUtils.odeQuatToJomlQuat(capsuleView.getQuaternion()).mul(new Quaterniond(0.707,0,0,0.707))); modelTransformMatrix.rotate(PhysicsUtils.odeQuatToJomlQuat(capsuleView.getQuaternion()).mul(new Quaterniond(0.707,0,0,0.707)));
modelTransformMatrix.scale(capsuleView.getRadius(),capsuleView.getLength(),capsuleView.getRadius()); //the ode4j capsule's end caps are always at least radius length, the length only controls the distance between the two caps.
//unfortunately that won't be easy to replicate with rendering tech currently; instead, run logic below
double radius = capsuleView.getRadius();
double length = capsuleView.getLength();
if(length < radius) length = radius;
modelTransformMatrix.scale(radius,length,radius);
hitboxModel.setModelMatrix(modelTransformMatrix); hitboxModel.setModelMatrix(modelTransformMatrix);
hitboxModel.draw(renderPipelineState,openGLState); hitboxModel.draw(renderPipelineState,openGLState);
} }
@ -220,7 +225,12 @@ public class DebugContentPipeline implements RenderPipeline {
modelTransformMatrix.translate(cameraModifiedPosition); modelTransformMatrix.translate(cameraModifiedPosition);
//since you're directly accessing the quat from the body, need to adjust it to be in the correct orientation //since you're directly accessing the quat from the body, need to adjust it to be in the correct orientation
modelTransformMatrix.rotate(PhysicsUtils.odeQuatToJomlQuat(capsuleView.getQuaternion()).mul(new Quaterniond(0.707,0,0,0.707))); modelTransformMatrix.rotate(PhysicsUtils.odeQuatToJomlQuat(capsuleView.getQuaternion()).mul(new Quaterniond(0.707,0,0,0.707)));
modelTransformMatrix.scale(capsuleView.getRadius(),capsuleView.getLength(),capsuleView.getRadius()); //the ode4j capsule's end caps are always at least radius length, the length only controls the distance between the two caps.
//unfortunately that won't be easy to replicate with rendering tech currently; instead, run logic below
double radius = capsuleView.getRadius();
double length = capsuleView.getLength();
if(length < radius) length = radius;
modelTransformMatrix.scale(radius,length,radius);
hitboxModel.setModelMatrix(modelTransformMatrix); hitboxModel.setModelMatrix(modelTransformMatrix);
hitboxModel.draw(renderPipelineState,openGLState); hitboxModel.draw(renderPipelineState,openGLState);
} }

View File

@ -0,0 +1,41 @@
package electrosphere.server;
import electrosphere.engine.Globals;
import electrosphere.logger.LoggerInterface;
/**
* Functions that should be fired every server frame
*/
public class MainServerFunctions {
/**
* Calls the main server routines that should fire each frame
*/
public static void simulate(){
//
//Synchronous player message parsing\
Globals.profiler.beginCpuSample("Server synchronous packet parsing");
if(Globals.server != null){
Globals.server.synchronousPacketHandling();
}
Globals.profiler.endCpuSample();
//
//Micro simulation (ie simulating each scene on the server)
Globals.profiler.beginCpuSample("Server micro simulation");
LoggerInterface.loggerEngine.DEBUG("Begin server micro simulation");
if(Globals.realmManager != null){
Globals.realmManager.simulate();
}
//
//Macro simulation (ie simulating the larger world macro data)
LoggerInterface.loggerEngine.DEBUG("Server macro simulation");
if(Globals.macroSimulation != null && Globals.macroSimulation.isReady()){
Globals.macroSimulation.simulate();
}
Globals.profiler.endCpuSample();
}
}

View File

@ -30,7 +30,7 @@ public class PlayerCharacterCreation {
Realm realm = Globals.realmManager.getRealms().iterator().next(); Realm realm = Globals.realmManager.getRealms().iterator().next();
Entity newPlayerEntity = CreatureUtils.serverSpawnBasicCreature(realm,new Vector3d(Globals.spawnPoint.x,Globals.spawnPoint.y,Globals.spawnPoint.z),raceName,template); Entity newPlayerEntity = CreatureUtils.serverSpawnBasicCreature(realm,new Vector3d(Globals.spawnPoint.x,Globals.spawnPoint.y,Globals.spawnPoint.z),raceName,template);
int playerCharacterId = newPlayerEntity.getId(); int playerCharacterId = newPlayerEntity.getId();
connectionHandler.setPlayerCharacterId(playerCharacterId); connectionHandler.setPlayerEntityId(playerCharacterId);
CreatureUtils.setControllerPlayerId(newPlayerEntity, connectionHandler.getPlayerId()); CreatureUtils.setControllerPlayerId(newPlayerEntity, connectionHandler.getPlayerId());
//custom player btrees //custom player btrees
addPlayerServerBTrees(newPlayerEntity); addPlayerServerBTrees(newPlayerEntity);

View File

@ -0,0 +1,35 @@
package electrosphere.server.player;
import electrosphere.client.item.ItemActions;
import electrosphere.entity.Entity;
import electrosphere.entity.state.block.ServerBlockTree;
import electrosphere.net.parser.net.message.InventoryMessage;
import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.server.datacell.utils.EntityLookupUtils;
/**
* Class for handling
*/
public class PlayerActions {
/**
* Attempts to perform an action a player requested
* @param connectionHandler The player's connection handler
* @param message The network message that encapsulates the requested action
*/
public static void attemptPlayerAction(ServerConnectionHandler connectionHandler, InventoryMessage message){
Entity playerEntity = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
if(message.getitemActionCode() == ItemActions.ITEM_ACTION_CODE_SECONDARY){
ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(playerEntity);
if(serverBlockTree != null){
if(message.getitemActionCodeState() == ItemActions.ITEM_ACTION_CODE_STATE_ON){
serverBlockTree.start();
} else {
serverBlockTree.stop();
}
}
}
}
}