2022-10-31 22:32:07 +00:00
# include "ControlBehaviors.h"
# include "AMFFormat.h"
# include "Entity.h"
# include "Game.h"
# include "GameMessages.h"
# include "ModelComponent.h"
2022-11-27 09:24:35 +00:00
# include "../../dWorldServer/ObjectIDManager.h"
2022-10-31 22:32:07 +00:00
# include "dLogger.h"
2023-02-14 02:55:44 +00:00
# include "BehaviorStates.h"
# include "AssetManager.h"
# include "BlockDefinition.h"
2023-01-07 05:17:05 +00:00
# include "User.h"
2023-02-14 02:55:44 +00:00
# include "tinyxml2.h"
# include "CDClientDatabase.h"
// Message includes
# include "AddActionMessage.h"
# include "AddStripMessage.h"
# include "AddMessage.h"
# include "MigrateActionsMessage.h"
# include "MoveToInventoryMessage.h"
# include "MergeStripsMessage.h"
# include "RearrangeStripMessage.h"
# include "RemoveActionsMessage.h"
# include "RemoveStripMessage.h"
# include "RenameMessage.h"
# include "SplitStripMessage.h"
# include "UpdateActionMessage.h"
# include "UpdateStripUiMessage.h"
void ControlBehaviors : : RequestUpdatedID ( int32_t behaviorID , ModelComponent * modelComponent , Entity * modelOwner , const SystemAddress & sysAddr ) {
2022-11-27 09:24:35 +00:00
// auto behavior = modelComponent->FindBehavior(behaviorID);
// if (behavior->GetBehaviorID() == -1 || behavior->GetShouldSetNewID()) {
// ObjectIDManager::Instance()->RequestPersistentID(
// [behaviorID, behavior, modelComponent, modelOwner, sysAddr](uint32_t persistentId) {
// behavior->SetShouldGetNewID(false);
// behavior->SetIsTemplated(false);
// behavior->SetBehaviorID(persistentId);
// // This updates the behavior ID of the behavior should this be a new behavior
// AMFArrayValue args;
// AMFStringValue* behaviorIDString = new AMFStringValue();
// behaviorIDString->SetStringValue(std::to_string(persistentId));
// args.InsertValue("behaviorID", behaviorIDString);
// AMFStringValue* objectIDAsString = new AMFStringValue();
// objectIDAsString->SetStringValue(std::to_string(modelComponent->GetParent()->GetObjectID()));
// args.InsertValue("objectID", objectIDAsString);
// GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorID", &args);
// ControlBehaviors::SendBehaviorListToClient(modelComponent->GetParent(), sysAddr, modelOwner);
// });
// }
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : SendBehaviorListToClient ( Entity * modelEntity , const SystemAddress & sysAddr , Entity * modelOwner ) {
2022-10-31 22:32:07 +00:00
auto * modelComponent = modelEntity - > GetComponent < ModelComponent > ( ) ;
if ( ! modelComponent ) return ;
2022-11-27 09:24:35 +00:00
AMFArrayValue behaviorsToSerialize ;
2022-10-31 22:32:07 +00:00
AMFArrayValue * behaviors = new AMFArrayValue ( ) ; // Empty for now
/**
* The behaviors AMFArray will have up to 5 elements in the dense portion .
* Each element in the dense portion will be made up of another AMFArray
* with the following information mapped in the associative portion
* " id " : Behavior ID cast to an AMFString
* " isLocked " : AMFTrue or AMFFalse of whether or not the behavior is locked
* " isLoot " : AMFTrue or AMFFalse of whether or not the behavior is a custom behavior ( true if custom )
* " name " : The name of the behavior formatted as an AMFString
*/
behaviorsToSerialize . InsertValue ( " behaviors " , behaviors ) ;
AMFStringValue * amfStringValueForObjectID = new AMFStringValue ( ) ;
amfStringValueForObjectID - > SetStringValue ( std : : to_string ( modelComponent - > GetParent ( ) - > GetObjectID ( ) ) ) ;
behaviorsToSerialize . InsertValue ( " objectID " , amfStringValueForObjectID ) ;
GameMessages : : SendUIMessageServerToSingleClient ( modelOwner , sysAddr , " UpdateBehaviorList " , & behaviorsToSerialize ) ;
}
2022-11-27 09:24:35 +00:00
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : ModelTypeChanged ( AMFArrayValue * arguments , ModelComponent * ModelComponent ) {
2022-11-27 09:24:35 +00:00
auto * modelTypeAmf = arguments - > FindValue < AMFDoubleValue > ( " ModelType " ) ;
if ( ! modelTypeAmf ) return ;
uint32_t modelType = static_cast < uint32_t > ( modelTypeAmf - > GetDoubleValue ( ) ) ;
//TODO Update the model type here
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : ToggleExecutionUpdates ( ) {
2022-11-27 09:24:35 +00:00
//TODO do something with this info
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : AddStrip ( AMFArrayValue * arguments ) {
AddStripMessage addStripMessage ( arguments ) ;
2022-11-27 09:24:35 +00:00
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : RemoveStrip ( AMFArrayValue * arguments ) {
RemoveStripMessage removeStrip ( arguments ) ;
2022-11-27 09:24:35 +00:00
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : MergeStrips ( AMFArrayValue * arguments ) {
MergeStripsMessage mergeStripsMessage ( arguments ) ;
2022-11-27 09:24:35 +00:00
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : SplitStrip ( AMFArrayValue * arguments ) {
SplitStripMessage splitStripMessage ( arguments ) ;
2022-11-27 09:24:35 +00:00
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : UpdateStripUI ( AMFArrayValue * arguments ) {
UpdateStripUiMessage updateStripUiMessage ( arguments ) ;
2022-11-27 09:24:35 +00:00
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : AddAction ( AMFArrayValue * arguments ) {
AddActionMessage addActionMessage ( arguments ) ;
2022-11-27 09:24:35 +00:00
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : MigrateActions ( AMFArrayValue * arguments ) {
MigrateActionsMessage migrateActionsMessage ( arguments ) ;
2022-11-27 09:24:35 +00:00
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : RearrangeStrip ( AMFArrayValue * arguments ) {
RearrangeStripMessage rearrangeStripMessage ( arguments ) ;
2022-11-27 09:24:35 +00:00
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : Add ( AMFArrayValue * arguments ) {
AddMessage addMessage ( arguments ) ;
2022-11-27 09:24:35 +00:00
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : RemoveActions ( AMFArrayValue * arguments ) {
RemoveActionsMessage removeActionsMessage ( arguments ) ;
2022-11-27 09:24:35 +00:00
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : Rename ( Entity * modelEntity , const SystemAddress & sysAddr , Entity * modelOwner , AMFArrayValue * arguments ) {
RenameMessage renameMessage ( arguments ) ;
2022-11-27 09:24:35 +00:00
}
// TODO This is also supposed to serialize the state of the behaviors in progress but those aren't implemented yet
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : SendBehaviorBlocksToClient ( ModelComponent * modelComponent , const SystemAddress & sysAddr , Entity * modelOwner , AMFArrayValue * arguments ) {
// uint32_t behaviorID = ControlBehaviors::GetBehaviorIDFromArgument(arguments);
2022-11-27 09:24:35 +00:00
// auto modelBehavior = modelComponent->FindBehavior(behaviorID);
// if (!modelBehavior) return;
// modelBehavior->VerifyStates();
// auto states = modelBehavior->GetBehaviorStates();
// // Begin serialization.
// /**
// * for each state
// * strip id
// * ui info
// * x
// * y
// * actions
// * action1
// * action2
// * ...
// * behaviorID of strip
// * objectID of strip
// */
// LWOOBJID targetObjectID = LWOOBJID_EMPTY;
// behaviorID = 0;
// AMFArrayValue behaviorInfo;
// AMFArrayValue* stateSerialize = new AMFArrayValue();
// for (auto it = states.begin(); it != states.end(); it++) {
// Game::logger->Log("PropertyBehaviors", "Begin serialization of state %i!\n", it->first);
// AMFArrayValue* state = new AMFArrayValue();
// AMFDoubleValue* stateAsDouble = new AMFDoubleValue();
// stateAsDouble->SetDoubleValue(it->first);
// state->InsertValue("id", stateAsDouble);
// AMFArrayValue* strips = new AMFArrayValue();
// auto stripsInState = it->second->GetStrips();
// for (auto strip = stripsInState.begin(); strip != stripsInState.end(); strip++) {
// Game::logger->Log("PropertyBehaviors", "Begin serialization of strip %i!\n", strip->first);
// AMFArrayValue* thisStrip = new AMFArrayValue();
// AMFDoubleValue* stripID = new AMFDoubleValue();
// stripID->SetDoubleValue(strip->first);
// thisStrip->InsertValue("id", stripID);
// AMFArrayValue* uiArray = new AMFArrayValue();
// AMFDoubleValue* yPosition = new AMFDoubleValue();
// yPosition->SetDoubleValue(strip->second->GetYPosition());
// uiArray->InsertValue("y", yPosition);
// AMFDoubleValue* xPosition = new AMFDoubleValue();
// xPosition->SetDoubleValue(strip->second->GetXPosition());
// uiArray->InsertValue("x", xPosition);
// thisStrip->InsertValue("ui", uiArray);
// targetObjectID = modelComponent->GetParent()->GetObjectID();
// behaviorID = modelBehavior->GetBehaviorID();
// AMFArrayValue* stripSerialize = new AMFArrayValue();
// for (auto behaviorAction : strip->second->GetActions()) {
// Game::logger->Log("PropertyBehaviors", "Begin serialization of action %s!\n", behaviorAction->actionName.c_str());
// AMFArrayValue* thisAction = new AMFArrayValue();
// AMFStringValue* actionName = new AMFStringValue();
// actionName->SetStringValue(behaviorAction->actionName);
// thisAction->InsertValue("Type", actionName);
// if (behaviorAction->parameterValueString != "")
// {
// AMFStringValue* valueAsString = new AMFStringValue();
// valueAsString->SetStringValue(behaviorAction->parameterValueString);
// thisAction->InsertValue(behaviorAction->parameterName, valueAsString);
// }
// else if (behaviorAction->parameterValueDouble != 0.0)
// {
// AMFDoubleValue* valueAsDouble = new AMFDoubleValue();
// valueAsDouble->SetDoubleValue(behaviorAction->parameterValueDouble);
// thisAction->InsertValue(behaviorAction->parameterName, valueAsDouble);
// }
// stripSerialize->PushBackValue(thisAction);
// }
// thisStrip->InsertValue("actions", stripSerialize);
// strips->PushBackValue(thisStrip);
// }
// state->InsertValue("strips", strips);
// stateSerialize->PushBackValue(state);
// }
// behaviorInfo.InsertValue("states", stateSerialize);
// AMFStringValue* objectidAsString = new AMFStringValue();
// objectidAsString->SetStringValue(std::to_string(targetObjectID));
// behaviorInfo.InsertValue("objectID", objectidAsString);
// AMFStringValue* behaviorIDAsString = new AMFStringValue();
// behaviorIDAsString->SetStringValue(std::to_string(behaviorID));
// behaviorInfo.InsertValue("BehaviorID", behaviorIDAsString);
// GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorBlocks", &behaviorInfo);
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : UpdateAction ( AMFArrayValue * arguments ) {
UpdateActionMessage updateActionMessage ( arguments ) ;
auto * blockDefinition = GetBlockInfo ( updateActionMessage . GetType ( ) ) ;
if ( updateActionMessage . GetValueParameterString ( ) . size ( ) > 0 ) {
if ( updateActionMessage . GetValueParameterString ( ) . size ( ) < blockDefinition - > GetMinimumValue ( ) | |
updateActionMessage . GetValueParameterString ( ) . size ( ) > blockDefinition - > GetMaximumValue ( ) ) {
Game : : logger - > Log ( " ControlBehaviors " , " Updated block %s is out of range. Ignoring update " , updateActionMessage . GetType ( ) . c_str ( ) ) ;
return ;
}
} else {
if ( updateActionMessage . GetValueParameterDouble ( ) < blockDefinition - > GetMinimumValue ( ) | |
updateActionMessage . GetValueParameterDouble ( ) > blockDefinition - > GetMaximumValue ( ) ) {
Game : : logger - > Log ( " ControlBehaviors " , " Updated block %s is out of range. Ignoring update " , updateActionMessage . GetType ( ) . c_str ( ) ) ;
return ;
2022-11-27 09:24:35 +00:00
}
}
}
2023-02-14 02:55:44 +00:00
void ControlBehaviors : : MoveToInventory ( ModelComponent * modelComponent , const SystemAddress & sysAddr , Entity * modelOwner , AMFArrayValue * arguments ) {
2022-11-27 09:24:35 +00:00
// This closes the UI menu should it be open while the player is removing behaviors
AMFArrayValue args ;
AMFFalseValue * stateToPop = new AMFFalseValue ( ) ;
args . InsertValue ( " visible " , stateToPop ) ;
GameMessages : : SendUIMessageServerToSingleClient ( modelOwner , modelOwner - > GetParentUser ( ) - > GetSystemAddress ( ) , " ToggleBehaviorEditor " , & args ) ;
2023-02-14 02:55:44 +00:00
MoveToInventoryMessage moveToInventoryMessage ( arguments ) ;
2022-11-27 09:24:35 +00:00
SendBehaviorListToClient ( modelComponent - > GetParent ( ) , sysAddr , modelOwner ) ;
}
void ControlBehaviors : : ProcessCommand ( Entity * modelEntity , const SystemAddress & sysAddr , AMFArrayValue * arguments , std : : string command , Entity * modelOwner ) {
2023-02-14 02:55:44 +00:00
if ( ! isInitialized | | ! modelEntity | | ! modelOwner | | ! arguments ) return ;
2022-11-27 09:24:35 +00:00
auto * modelComponent = modelEntity - > GetComponent < ModelComponent > ( ) ;
if ( ! modelComponent ) return ;
if ( command = = " sendBehaviorListToClient " )
SendBehaviorListToClient ( modelEntity , sysAddr , modelOwner ) ;
else if ( command = = " modelTypeChanged " )
ModelTypeChanged ( arguments , modelComponent ) ;
else if ( command = = " toggleExecutionUpdates " )
ToggleExecutionUpdates ( ) ;
else if ( command = = " addStrip " )
AddStrip ( arguments ) ;
else if ( command = = " removeStrip " )
RemoveStrip ( arguments ) ;
else if ( command = = " mergeStrips " )
MergeStrips ( arguments ) ;
else if ( command = = " splitStrip " )
SplitStrip ( arguments ) ;
else if ( command = = " updateStripUI " )
UpdateStripUI ( arguments ) ;
else if ( command = = " addAction " )
AddAction ( arguments ) ;
else if ( command = = " migrateActions " )
MigrateActions ( arguments ) ;
else if ( command = = " rearrangeStrip " )
RearrangeStrip ( arguments ) ;
else if ( command = = " add " )
Add ( arguments ) ;
else if ( command = = " removeActions " )
RemoveActions ( arguments ) ;
else if ( command = = " rename " )
Rename ( modelEntity , sysAddr , modelOwner , arguments ) ;
else if ( command = = " sendBehaviorBlocksToClient " )
SendBehaviorBlocksToClient ( modelComponent , sysAddr , modelOwner , arguments ) ;
else if ( command = = " moveToInventory " )
MoveToInventory ( modelComponent , sysAddr , modelOwner , arguments ) ;
else if ( command = = " updateAction " )
UpdateAction ( arguments ) ;
else
Game : : logger - > Log ( " ControlBehaviors " , " Unknown behavior command (%s) \n " , command . c_str ( ) ) ;
}
2023-02-14 02:55:44 +00:00
ControlBehaviors : : ControlBehaviors ( ) {
auto blocksDefStreamBuffer = Game : : assetManager - > GetFileAsBuffer ( " ui \\ ingame \\ blocksdef.xml " ) ;
if ( ! blocksDefStreamBuffer . m_Success ) {
Game : : logger - > Log ( " ControlBehaviors " , " failed to open blocksdef " ) ;
return ;
}
std : : istream blocksBuffer ( & blocksDefStreamBuffer ) ;
if ( ! blocksBuffer . good ( ) ) {
Game : : logger - > Log ( " ControlBehaviors " , " Blocks buffer is not good! " ) ;
return ;
}
tinyxml2 : : XMLDocument m_Doc ;
std : : string read { } ;
std : : string buffer { } ;
bool commentBlockStart = false ;
while ( std : : getline ( blocksBuffer , read ) ) {
// tinyxml2 should handle comment blocks but the client has one that fails the processing.
// This preprocessing just removes all comments from the read file out of an abundance of caution.
if ( read . find ( " <!-- " ) ! = std : : string : : npos ) {
commentBlockStart = true ;
}
if ( read . find ( " --> " ) ! = std : : string : : npos ) {
commentBlockStart = false ;
continue ;
}
if ( ! commentBlockStart ) buffer + = read ;
}
auto ret = m_Doc . Parse ( buffer . c_str ( ) ) ;
if ( ret = = tinyxml2 : : XML_SUCCESS ) {
Game : : logger - > LogDebug ( " ControlBehaviors " , " Successfully parsed the blocksdef file! " ) ;
} else {
Game : : logger - > Log ( " Character " , " Failed to parse BlocksDef xmlData due to error %i! " , ret ) ;
return ;
}
auto * blockLibrary = m_Doc . FirstChildElement ( ) ;
if ( ! blockLibrary ) {
Game : : logger - > Log ( " ControlBehaviors " , " No Block Library child element found. " ) ;
return ;
}
// Now parse the blocksdef for the cheat detection server side.
// The client does these checks, but a bad actor can bypass the client checks
auto * blockSections = blockLibrary - > FirstChildElement ( ) ;
while ( blockSections ) {
auto * block = blockSections - > FirstChildElement ( ) ;
std : : string blockName { } ;
while ( block ) {
blockName = block - > Name ( ) ;
BlockDefinition * blockDefinition = new BlockDefinition ( ) ;
std : : string name { } ;
std : : string typeName { } ;
auto * argument = block - > FirstChildElement ( " Argument " ) ;
if ( argument ) {
auto * defaultDefinition = argument - > FirstChildElement ( " DefaultValue " ) ;
if ( defaultDefinition ) blockDefinition - > SetDefaultValue ( defaultDefinition - > GetText ( ) ) ;
auto * typeDefinition = argument - > FirstChildElement ( " Type " ) ;
if ( typeDefinition ) typeName = typeDefinition - > GetText ( ) ;
auto * nameDefinition = argument - > FirstChildElement ( " Name " ) ;
if ( nameDefinition ) name = nameDefinition - > GetText ( ) ;
// Now we parse the blocksdef file for the relevant information
if ( typeName = = " String " ) {
blockDefinition - > SetMaximumValue ( 50 ) ; // The client has a hardcoded limit of 50 characters in a string field
} else if ( typeName = = " Float " | | typeName = = " Integer " ) {
auto * maximumDefinition = argument - > FirstChildElement ( " Maximum " ) ;
if ( maximumDefinition ) blockDefinition - > SetMaximumValue ( std : : stof ( maximumDefinition - > GetText ( ) ) ) ;
auto * minimumDefinition = argument - > FirstChildElement ( " Minimum " ) ;
if ( minimumDefinition ) blockDefinition - > SetMinimumValue ( std : : stof ( minimumDefinition - > GetText ( ) ) ) ;
} else if ( typeName = = " Enumeration " ) {
auto * values = argument - > FirstChildElement ( " Values " ) ;
if ( values ) {
auto * value = values - > FirstChildElement ( " Value " ) ;
while ( value ) {
if ( value - > GetText ( ) = = blockDefinition - > GetDefaultValue ( ) ) blockDefinition - > GetDefaultValue ( ) = std : : to_string ( blockDefinition - > GetMaximumValue ( ) ) ;
blockDefinition - > SetMaximumValue ( blockDefinition - > GetMaximumValue ( ) + 1 ) ;
value = value - > NextSiblingElement ( " Value " ) ;
}
blockDefinition - > SetMaximumValue ( blockDefinition - > GetMaximumValue ( ) - 1 ) ; // Maximum value is 0 indexed
} else {
values = argument - > FirstChildElement ( " EnumerationSource " ) ;
if ( ! values ) {
Game : : logger - > Log ( " ControlBehaviors " , " Failed to parse EnumerationSource from block (%s) " , blockName . c_str ( ) ) ;
continue ;
}
auto * serviceNameNode = values - > FirstChildElement ( " ServiceName " ) ;
if ( ! serviceNameNode ) {
Game : : logger - > Log ( " ControlBehaviors " , " Failed to parse ServiceName from block (%s) " , blockName . c_str ( ) ) ;
continue ;
}
std : : string serviceName = serviceNameNode - > GetText ( ) ;
if ( serviceName = = " GetBehaviorSoundList " ) {
auto res = CDClientDatabase : : ExecuteQuery ( " SELECT MAX(id) as countSounds FROM UGBehaviorSounds; " ) ;
blockDefinition - > SetMaximumValue ( res . getIntField ( " countSounds " ) ) ;
blockDefinition - > SetDefaultValue ( " 0 " ) ;
} else {
Game : : logger - > Log ( " ControlBehaviors " , " Unsupported Enumeration ServiceType (%s) " , serviceName . c_str ( ) ) ;
continue ;
}
}
} else {
Game : : logger - > Log ( " ControlBehaviors " , " Unsupported block value type (%s)! " , typeName . c_str ( ) ) ;
continue ;
}
}
blockTypes . insert ( std : : make_pair ( blockName , blockDefinition ) ) ;
block = block - > NextSiblingElement ( ) ;
}
blockSections = blockSections - > NextSiblingElement ( ) ;
}
isInitialized = true ;
Game : : logger - > LogDebug ( " ControlBehaviors " , " Created all base block classes " ) ;
for ( auto b : blockTypes ) {
Game : : logger - > LogDebug ( " ControlBehaviors " , " block name is %s default %s min %f max %f " , b . first . c_str ( ) , b . second - > GetDefaultValue ( ) . c_str ( ) , b . second - > GetMinimumValue ( ) , b . second - > GetMaximumValue ( ) ) ;
}
}
BlockDefinition * ControlBehaviors : : GetBlockInfo ( const BlockName & blockName ) {
auto blockDefinition = blockTypes . find ( blockName ) ;
return blockDefinition ! = blockTypes . end ( ) ? blockDefinition - > second : & BlockDefinition : : blockDefinitionDefault ;
}