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",
"offsetVector" : [0,0,0],
"offsetRotation" : [-0.334,0.145,-0.28,0.89],
"canBlock" : true,
"equipClassWhitelist" : [
"tool",
"weapon",
@ -284,6 +285,22 @@
]
}
],
"blockSystem" : {
"variants": [
{
"variantId": "blockWeaponRight",
"windUpAnimation" : "Jump",
"mainAnimation" : "Fall",
"cooldownAnimation" : "Land",
"defaults" : [
{
"equipPoint" : "handRight",
"itemClassEquipped" : "weapon"
}
]
}
]
},
"collidable" : {
"type" : "CYLINDER",
"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)
Develop debug ui for equip points
(06/18/2024)
Block state synchronization between client and server
# TODO
@ -375,15 +378,16 @@ Audio FX for everything
= Coding =
Sour spot, sweet spot for damage hitboxes and hurtboxes
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)
- Introduce block hitbox (blockbox) type
Enemy AI
Probably some kind of tutorial text
Network-able ui messages
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

View File

@ -24,6 +24,14 @@
{
"name" : "containerType",
"type" : "FIXED_INT"
},
{
"name" : "itemActionCode",
"type" : "FIXED_INT"
},
{
"name" : "itemActionCodeState",
"type" : "FIXED_INT"
}
],
"messageTypes" : [
@ -81,6 +89,15 @@
"data" : [
"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 electrosphere.audio.VirtualAudioSourceManager.VirtualAudioSourceType;
import electrosphere.client.item.ItemActions;
import electrosphere.client.targeting.crosshair.Crosshair;
import electrosphere.client.terrain.editing.TerrainEditing;
import electrosphere.collision.CollisionEngine;
@ -85,7 +86,6 @@ import electrosphere.entity.state.attack.ShooterTree;
import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.entity.state.ironsight.IronSightTree;
import electrosphere.entity.state.movement.JumpTree;
import electrosphere.entity.state.movement.SprintTree;
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_INVENTORY_OPEN = "inventoryOpen";
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_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_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_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
@ -837,22 +837,12 @@ public class ControlHandler {
}
}});
mainGameControlList.add(controls.get(INPUT_CODE_IRON_SIGHT));
controls.get(INPUT_CODE_IRON_SIGHT).setOnPress(new ControlMethod() {public void execute() {
if(Globals.playerEntity != null){
IronSightTree ironSightTree = IronSightTree.getIronSightTree(Globals.playerEntity);
if(ironSightTree != null){
ironSightTree.start();
}
}
mainGameControlList.add(controls.get(ITEM_SECONDARY));
controls.get(ITEM_SECONDARY).setOnPress(new ControlMethod() {public void execute() {
ItemActions.attemptSecondaryItemAction();
}});
controls.get(INPUT_CODE_IRON_SIGHT).setOnRelease(new ControlMethod() {public void execute() {
if(Globals.playerEntity != null){
IronSightTree ironSightTree = IronSightTree.getIronSightTree(Globals.playerEntity);
if(ironSightTree != null){
ironSightTree.release();
}
}
controls.get(ITEM_SECONDARY).setOnRelease(new ControlMethod() {public void execute() {
ItemActions.releaseSecondaryItemAction();
}});
/*

View File

@ -20,6 +20,7 @@ import electrosphere.game.server.world.MacroData;
import electrosphere.logger.LoggerInterface;
import electrosphere.menu.debug.ImGuiWindowMacros;
import electrosphere.renderer.RenderingEngine;
import electrosphere.server.MainServerFunctions;
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");
LoggerInterface.loggerEngine.DEBUG("Begin server micro simulation");
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.beginCpuSample("Main Server Functions");
MainServerFunctions.simulate();
Globals.profiler.endCpuSample();
}

View File

@ -272,6 +272,12 @@ public class EntityDataStrings {
*/
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
*/

View File

@ -1,27 +1,224 @@
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.SynchronizableEnum;
import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree;
import electrosphere.renderer.actor.Actor;
@SynchronizedBehaviorTree(name = "clientBlockTree", isServer = false, correspondingTree="serverBlockTree")
/**
* Client block tree
*/
public class ClientBlockTree {
public class ClientBlockTree implements BehaviorTree {
@SynchronizableEnum
/**
* The state of the block tree
*/
public enum BlockState {
WIND_UP,
BLOCKING,
COOLDOWN,
NOT_BLOCKING,
}
@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;
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.game.data.creature.type.block.BlockSystem;
import electrosphere.net.synchronization.annotation.SyncedField;
import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree;
@ -8,9 +23,175 @@ import electrosphere.net.synchronization.annotation.SynchronizedBehaviorTree;
/**
* Server block tree
*/
public class ServerBlockTree {
public class ServerBlockTree implements BehaviorTree {
@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();
boolean itemIsInPointWhitelist = pointEquipClassList.contains(equipItemClass);
if(!hasEquipped && targetIsItem && !targetIsAttached && itemIsInPointWhitelist){
//
//visual transforms
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);
List<EquipWhitelist> whitelist = ItemUtils.getEquipWhitelist(toEquip);
for(EquipWhitelist whitelistItem : whitelist){
@ -141,7 +144,7 @@ public class ClientEquipState implements BehaviorTree {
}
}
} 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);
if(Globals.controlHandler.cameraIsThirdPerson()){
AttachUtils.clientAttachEntityToEntityAtBone(parent, toEquip, point.getBone(), AttachUtils.getEquipPointRotationOffset(point.getOffsetRotation()));
@ -284,8 +287,11 @@ public class ClientEquipState implements BehaviorTree {
Entity equipped = equipMap.remove(pointId);
if(equipped != null){
boolean targetHasWhitelist = ItemUtils.hasEquipList(equipped);
//
//visual transforms
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);
List<EquipWhitelist> whitelist = ItemUtils.getEquipWhitelist(equipped);
for(EquipWhitelist whitelistItem : whitelist){
@ -304,6 +310,7 @@ public class ClientEquipState implements BehaviorTree {
}
}
} else {
//does not depend on the type of creature
AttachUtils.clientDetatchEntityFromEntityAtBone(parent, equipped);
EntityUtils.cleanUpEntity(equipped);
}

View File

@ -21,6 +21,7 @@ import electrosphere.entity.EntityDataStrings;
import electrosphere.entity.EntityTags;
import electrosphere.entity.EntityUtils;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.state.block.ServerBlockTree;
import electrosphere.entity.state.gravity.GravityUtils;
import electrosphere.entity.state.inventory.InventoryUtils;
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.creature.CreatureUtils;
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.item.type.EquipWhitelist;
import electrosphere.net.parser.net.message.InventoryMessage;
@ -46,9 +49,12 @@ import electrosphere.server.datacell.utils.ServerEntityTagUtils;
*/
public class ServerEquipState implements BehaviorTree {
//the parent entity of this equip state
Entity parent;
//the list of available equip points
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>();
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){
boolean hasEquipped = hasEquippedAtPoint(point.getEquipPointId());
boolean targetIsItem = ItemUtils.isItem(inInventoryEntity);
@ -91,8 +102,10 @@ public class ServerEquipState implements BehaviorTree {
//bind in world with in inventory
ItemUtils.setRealWorldEntity(inInventoryEntity, inWorldItem);
//
//Visual transforms
if(targetHasWhitelist){
//by attaching are we going to be replacing meshes?
//depends on the type of creature
String parentCreatureId = CreatureUtils.getType(parent);
List<EquipWhitelist> whitelist = ItemUtils.getEquipWhitelist(inWorldItem);
for(EquipWhitelist whitelistItem : whitelist){
@ -117,7 +130,7 @@ public class ServerEquipState implements BehaviorTree {
}
}
} 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);
AttachUtils.serverAttachEntityToEntityAtBone(parent, inWorldItem, point.getBone(), AttachUtils.getEquipPointRotationOffset(point.getOffsetRotation()));
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);
GravityUtils.serverAttemptDeactivateGravity(inWorldItem);
}
//
//update block state based on what we have equipped
this.updateBlockVariant();
//we need to send two packets
//1) Remove item from original 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){
for(EquipPoint point : equipPoints){
if(point.getEquipPointId().equals(name)){
@ -180,6 +203,11 @@ public class ServerEquipState implements BehaviorTree {
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){
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){
boolean hasEquipped = hasEquippedAtPoint(pointId);
if(hasEquipped){
@ -278,10 +310,17 @@ public class ServerEquipState implements BehaviorTree {
// 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){
Entity equipped = equipMap.remove(pointId);
if(equipped != null){
boolean targetHasWhitelist = ItemUtils.hasEquipList(equipped);
//
//Visual transforms
if(targetHasWhitelist){
//have to do fancy mesh removal nonsense
//basically the reverse of below
@ -319,13 +358,50 @@ public class ServerEquipState implements BehaviorTree {
AttachUtils.serverDetatchEntityFromEntityAtBone(parent, 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){
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
public void simulate(float deltaTime) {
}

View File

@ -215,7 +215,7 @@ public class ServerGravityTree implements BehaviorTree {
public void setState(GravityTreeState state){
this.state = 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>

View File

@ -215,4 +215,14 @@ public class ClientIdleTree implements BehaviorTree {
public void setState(IdleTreeState 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){
this.state = 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;
import electrosphere.server.datacell.utils.ServerBehaviorTreeUtils;
import electrosphere.net.parser.net.message.SynchronizationMessage;
import electrosphere.net.synchronization.BehaviorTreeIdEnums;
@ -676,7 +678,7 @@ public class ServerGroundMovementTree implements BehaviorTree {
public void setFacing(MovementRelativeFacing facing){
this.facing = 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);
}
/**
* 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){
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){
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){
if(values.size() > 0){
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.ServerAttackTree;
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.ServerCollidableTree;
import electrosphere.entity.state.equip.ClientEquipState;
@ -229,6 +231,9 @@ public class CreatureUtils {
ClientEquipState.attachTree(rVal, 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()){
switch(token){
case "BLENDER_TRANSFORM":
@ -517,6 +522,9 @@ public class CreatureUtils {
ServerEquipState.setEquipState(rVal, new ServerEquipState(rVal,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()){
switch(token){
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.creature.type.attack.AttackMove;
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.movement.MovementSystem;
import electrosphere.game.data.creature.type.rotator.RotatorSystem;
@ -29,6 +30,7 @@ public class CreatureType {
String modelPath;
ViewModelData viewModelData;
IdleData idleData;
BlockSystem blockSystem;
AttackMoveResolver attackMoveResolver;
@ -95,6 +97,10 @@ public class CreatureType {
public IdleData getIdleData(){
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;
//the rotation to apply to the items that are attached to the bone
List<Float> offsetRotation;
//signals that this equip point can block
boolean canBlock;
//the equip classes that are whitelisted for this equip point
List<String> equipClassWhitelist;
@ -68,6 +70,14 @@ public class EquipPoint {
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
* @return the classes

View File

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

View File

@ -1,14 +1,6 @@
package electrosphere.net.client.protocol;
import electrosphere.client.scene.ClientWorldData;
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.CharacterMessage;
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.SynchronizationMessage;
import electrosphere.net.parser.net.message.TerrainMessage;
import org.joml.Vector2f;
import org.joml.Vector3f;
public class ClientProtocol {

View File

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

View File

@ -15,6 +15,7 @@ public class InventoryMessage extends NetworkMessage {
SERVERCOMMANDEQUIPITEM,
SERVERCOMMANDUNEQUIPITEM,
CLIENTREQUESTUNEQUIPITEM,
CLIENTREQUESTPERFORMITEMACTION,
}
InventoryMessageType messageType;
@ -23,6 +24,8 @@ public class InventoryMessage extends NetworkMessage {
int entityId;
int equipperId;
int containerType;
int itemActionCode;
int itemActionCodeState;
InventoryMessage(InventoryMessageType messageType){
this.type = MessageType.INVENTORY_MESSAGE;
@ -73,6 +76,22 @@ public class InventoryMessage extends NetworkMessage {
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){
byteBuffer.read(2);
}
@ -97,6 +116,8 @@ public class InventoryMessage extends NetworkMessage {
return InventoryMessage.canParseserverCommandUnequipItemMessage(byteBuffer);
case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTUNEQUIPITEM:
return InventoryMessage.canParseclientRequestUnequipItemMessage(byteBuffer);
case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION:
return InventoryMessage.canParseclientRequestPerformItemActionMessage(byteBuffer);
}
return false;
}
@ -363,6 +384,49 @@ public class InventoryMessage extends NetworkMessage {
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
void serialize(){
byte[] intValues = new byte[8];
@ -505,6 +569,29 @@ public class InventoryMessage extends NetworkMessage {
rawBytes[6+i] = stringBytes[i];
}
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;
}

View File

@ -311,6 +311,11 @@ SYNCHRONIZATION_MESSAGE,
rVal = InventoryMessage.parseclientRequestUnequipItemMessage(byteBuffer);
}
break;
case TypeBytes.INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION:
if(InventoryMessage.canParseMessage(byteBuffer,secondByte)){
rVal = InventoryMessage.parseclientRequestPerformItemActionMessage(byteBuffer);
}
break;
}
break;
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_SERVERCOMMANDUNEQUIPITEM = 5;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTUNEQUIPITEM = 6;
public static final byte INVENTORY_MESSAGE_TYPE_CLIENTREQUESTPERFORMITEMACTION = 7;
/*
Inventory packet sizes
*/

View File

@ -5,41 +5,45 @@ import electrosphere.engine.Main;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.NetUtils;
import electrosphere.net.parser.net.message.NetworkMessage;
import electrosphere.server.saves.SaveUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author amaterasu
* Lowest level networking class for the server
*/
public class Server implements Runnable{
//the port the server is running on
int port;
//the socket for the server
ServerSocket serverSocket;
//the map of ip->connection handler
Map<String,ServerConnectionHandler> clientMap = new HashMap<String,ServerConnectionHandler>();
/**
* Inits the server
*/
void initServer(){
// clientMap = new HashMap<String,ServerConnectionHandler>();
}
/**
* Constructor
* @param port The port to run the server on
*/
public Server(int 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(){
try {
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){
for(ServerConnectionHandler client : clientMap.values()){
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){
ServerConnectionHandler newClient = new ServerConnectionHandler(serverInputStream,serverOutputStream);
clientMap.put("127.0.0.1", newClient);

View File

@ -1,21 +1,12 @@
package electrosphere.net.server;
import electrosphere.entity.types.creature.CreatureTemplate;
import electrosphere.entity.types.creature.CreatureUtils;
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.item.ItemUtils;
import electrosphere.entity.types.attach.AttachUtils;
import electrosphere.entity.types.collision.CollisionObjUtils;
import electrosphere.entity.ServerEntityUtils;
import electrosphere.logger.LoggerInterface;
import electrosphere.net.NetUtils;
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.PlayerMessage;
import electrosphere.net.parser.net.message.ServerMessage;
import electrosphere.net.parser.net.raw.NetworkParser;
import electrosphere.net.server.player.Player;
@ -24,30 +15,20 @@ import electrosphere.net.server.protocol.ServerProtocol;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Semaphore;
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;
/**
*
* @author satellite
* A connection to the server
*/
public class ServerConnectionHandler implements Runnable {
//the player id associated with this connection
static int playerIdIncrementer = 0;
//local carrier variables
@ -55,17 +36,30 @@ public class ServerConnectionHandler implements Runnable {
//socket carrier variables
Socket socket;
//the streams for the connection
// CryptoInputStream inputStream;
// CryptoOutputStream outputStream;
InputStream inputStream;
OutputStream outputStream;
boolean initialized;
boolean isAuthenticated = false;
//the network parser for the streams
NetworkParser networkParser;
//initialized status
boolean initialized;
//authentication status
boolean isAuthenticated = false;
//the player id
int playerID;
int playerCharacterID;
//the player's entity id
int playerEntityID;
//the creature template associated with this player
CreatureTemplate currentCreatureTemplate;
//the server protocol object associated with this player
ServerProtocol serverProtocol;
//thresholds for determining when to send pings and when a client has disconnected
@ -82,13 +76,27 @@ public class ServerConnectionHandler implements Runnable {
String netMonitorHandle;
//Used to copy messages from network parser to NetMonitor
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) {
this.socket = socket;
playerID = getNewPlayerID();
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){
this.local = true;
playerID = getNewPlayerID();
@ -114,6 +122,8 @@ public class ServerConnectionHandler implements Runnable {
} catch (SocketException ex) {
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 Properties properties = new Properties();
// final RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(4096, BigInteger.probablePrime(4000, new Random()));
@ -210,7 +220,13 @@ public class ServerConnectionHandler implements Runnable {
//ponder incoming messages
while(networkParser.hasIncomingMessaage()){
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){
@ -231,6 +247,18 @@ public class ServerConnectionHandler implements Runnable {
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){
playerID = id;
@ -245,12 +273,12 @@ public class ServerConnectionHandler implements Runnable {
return playerIdIncrementer;
}
public void setPlayerCharacterId(int id){
playerCharacterID = id;
public void setPlayerEntityId(int id){
playerEntityID = id;
}
public int getPlayerCharacterId(){
return playerCharacterID;
public int getPlayerEntityId(){
return playerEntityID;
}
public String getIPAddress(){
@ -266,7 +294,7 @@ public class ServerConnectionHandler implements Runnable {
}
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
Player playerObject = Globals.playerManager.getPlayerFromId(getPlayerId());
//get player entity & position
Entity playerEntity = playerObject.getPlayerEntity();
Vector3d position = EntityUtils.getPosition(playerEntity);
//deregister entity
EntityUtils.cleanUpEntity(playerObject.getPlayerEntity());
//TODO: tell all clients to destroy the entity
EntityUtils.cleanUpEntity(playerEntity);
//tell all clients to destroy the entity
ServerEntityUtils.destroyEntity(playerObject.getPlayerEntity());
}
}

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
* @param connectionHandler The connection handler for the player

View File

@ -52,5 +52,29 @@ public class EntityProtocol {
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;
import electrosphere.engine.Globals;
import electrosphere.entity.Entity;
import electrosphere.entity.state.equip.ClientEquipState;
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.net.parser.net.message.InventoryMessage;
import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.server.datacell.utils.EntityLookupUtils;
import electrosphere.server.player.PlayerActions;
/**
* Server protocol for dealing with inventory messages
*/
public class InventoryProtocol {
//the entity's equip inventory
public static final int INVENTORY_TYPE_EQUIP = 0;
//the natural inventory of the entity
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;
switch(message.getMessageSubtype()){
case ADDITEMTOINVENTORY:
LoggerInterface.loggerNetworking.DEBUG("[SERVER] ADD ITEM TO INVENTORY " + message.getentityId());
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerCharacterId());
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
if(InventoryUtils.hasNaturalInventory(target)){
InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
}
break;
case REMOVEITEMFROMINVENTORY:
LoggerInterface.loggerNetworking.DEBUG("[SERVER] REMOVE ITEM FROM INVENTORY " + message.getentityId());
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerCharacterId());
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
if(InventoryUtils.hasNaturalInventory(target)){
InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
}
break;
case CLIENTREQUESTEQUIPITEM:
LoggerInterface.loggerNetworking.DEBUG("[SERVER] REQUEST EQUIP ITEM " + message.getentityId());
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerCharacterId());
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
if(InventoryUtils.hasNaturalInventory(target)){
InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
}
break;
case CLIENTREQUESTUNEQUIPITEM:
LoggerInterface.loggerNetworking.DEBUG("[SERVER] REQUEST UNEQUIP ITEM " + message.getentityId());
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerCharacterId());
target = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
if(InventoryUtils.hasNaturalInventory(target)){
InventoryUtils.serverGetInventoryState(target).addNetworkMessage(message);
}
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 SERVERCOMMANDMOVEITEMCONTAINER:
case SERVERCOMMANDEQUIPITEM:

View File

@ -22,5 +22,19 @@ public class LoreProtocol {
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.server.ServerConnectionHandler;
/**
* Player protocol handling
*/
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){
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;
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.net.NetUtils;
import electrosphere.net.parser.net.message.AuthMessage;
import electrosphere.net.parser.net.message.CharacterMessage;
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.ServerMessage.ServerMessageType;
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 {
//the connection handler associated with this protocol object
ServerConnectionHandler connectionHandler;
//if set to true, will log ping messages
boolean echoPings = false;
/**
* Constructor
* @param connectionHandler the associated connection handler
*/
public ServerProtocol(ServerConnectionHandler 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
printMessage(message);
//actually handle message
switch(message.getType()){
case ENTITY_MESSAGE:
case ENTITY_MESSAGE: {
EntityProtocol.handleEntityMessage(connectionHandler, (EntityMessage)message);
break;
case TERRAIN_MESSAGE:
} break;
case TERRAIN_MESSAGE: {
TerrainProtocol.handleTerrainMessage(connectionHandler, (TerrainMessage)message);
break;
case PLAYER_MESSAGE:
} break;
case PLAYER_MESSAGE: {
PlayerProtocol.handlePlayerMessage(connectionHandler, (PlayerMessage)message);
break;
case AUTH_MESSAGE:
} break;
case AUTH_MESSAGE: {
AuthProtocol.handleAuthenticationMessage(connectionHandler, (AuthMessage)message);
break;
case SERVER_MESSAGE:
} break;
case SERVER_MESSAGE: {
handleServerMessage((ServerMessage)message);
break;
case CHARACTER_MESSAGE:
} break;
case CHARACTER_MESSAGE: {
CharacterProtocol.handleCharacterMessage(connectionHandler, (CharacterMessage)message);
break;
case LORE_MESSAGE:
} break;
case LORE_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;
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;
}
}
/**
* Handles a server-type network message
* @param message The server message
*/
void handleServerMessage(ServerMessage message){
switch(message.getMessageSubtype()){
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
*/

View File

@ -6,20 +6,25 @@ import java.nio.IntBuffer;
import org.joml.Vector3d;
import electrosphere.client.terrain.cache.ChunkData;
import electrosphere.engine.Globals;
import electrosphere.net.parser.net.message.TerrainMessage;
import electrosphere.net.server.Server;
import electrosphere.net.server.ServerConnectionHandler;
import electrosphere.net.server.player.Player;
import electrosphere.server.datacell.Realm;
import electrosphere.server.fluid.manager.ServerFluidChunk;
import electrosphere.server.terrain.editing.TerrainEditing;
import electrosphere.server.terrain.manager.ServerTerrainChunk;
import electrosphere.server.terrain.models.TerrainModification;
/**
* Server handling for terrain network messages
*/
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){
switch(message.getMessageSubtype()){
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){
/*
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){
// 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){
//world metadata
connectionHandler.addMessagetoOutgoingQueue(
@ -233,7 +279,8 @@ public class TerrainProtocol {
* @param message The message containing the edit request
*/
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_SERVERATTACKTREE_ID = 1;
public static final int BTREE_CLIENTEQUIPSTATE_ID = 2;
public static final int BTREE_SERVEREQUIPSTATE_ID = 3;
public static final int BTREE_GRAVITY_ID = 4;
public static final int BTREE_SERVERGRAVITY_ID = 5;
public static final int BTREE_IDLE_ID = 6;
public static final int BTREE_SERVERIDLE_ID = 7;
public static final int BTREE_CLIENTGROUNDMOVEMENTTREE_ID = 8;
public static final int BTREE_SERVERGROUNDMOVEMENTTREE_ID = 9;
public static final int BTREE_CLIENTBLOCKTREE_ID = 2;
public static final int BTREE_SERVERBLOCKTREE_ID = 3;
public static final int BTREE_CLIENTEQUIPSTATE_ID = 4;
public static final int BTREE_SERVEREQUIPSTATE_ID = 5;
public static final int BTREE_GRAVITY_ID = 6;
public static final int BTREE_SERVERGRAVITY_ID = 7;
public static final int BTREE_IDLE_ID = 8;
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;
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.ServerGroundMovementTree;
@ -110,13 +114,25 @@ public class ClientSynchronizationManager {
} 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: {
switch(message.getfieldId()){
}
} break;
case BehaviorTreeIdEnums.BTREE_SERVERGRAVITY_ID: {
switch(message.getfieldId()){
case 7:{
case 11:{
ClientGravityTree tree = ClientGravityTree.getClientGravityTree(entity);
tree.setState(ClientGravityTree.getGravityTreeStateShortAsEnum((short)message.getbTreeValue()));
} break;
@ -124,15 +140,15 @@ public class ClientSynchronizationManager {
} break;
case BehaviorTreeIdEnums.BTREE_SERVERIDLE_ID: {
switch(message.getfieldId()){
case 9:{
ClientIdleTree tree = ClientIdleTree.getIdleTree(entity);
case 13:{
ClientIdleTree tree = ClientIdleTree.getClientIdleTree(entity);
tree.setState(ClientIdleTree.getIdleTreeStateShortAsEnum((short)message.getbTreeValue()));
} break;
}
} break;
case BehaviorTreeIdEnums.BTREE_SERVERGROUNDMOVEMENTTREE_ID: {
switch(message.getfieldId()){
case 11:{
case 15:{
ClientGroundMovementTree tree = ClientGroundMovementTree.getClientGroundMovementTree(entity);
tree.setFacing(ClientGroundMovementTree.getMovementRelativeFacingShortAsEnum((short)message.getbTreeValue()));
} break;

View File

@ -116,7 +116,12 @@ public class DebugContentPipeline implements RenderPipeline {
modelTransformMatrix.translate(cameraModifiedPosition);
//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.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.draw(renderPipelineState,openGLState);
}
@ -220,7 +225,12 @@ public class DebugContentPipeline implements RenderPipeline {
modelTransformMatrix.translate(cameraModifiedPosition);
//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.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.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();
Entity newPlayerEntity = CreatureUtils.serverSpawnBasicCreature(realm,new Vector3d(Globals.spawnPoint.x,Globals.spawnPoint.y,Globals.spawnPoint.z),raceName,template);
int playerCharacterId = newPlayerEntity.getId();
connectionHandler.setPlayerCharacterId(playerCharacterId);
connectionHandler.setPlayerEntityId(playerCharacterId);
CreatureUtils.setControllerPlayerId(newPlayerEntity, connectionHandler.getPlayerId());
//custom player btrees
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();
}
}
}
}
}