diff --git a/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp b/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp index 0d0c4df7..0c0520a3 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyAvatar.cpp @@ -1592,6 +1592,60 @@ void cyAvatar::PlaySimpleAnimation(const plString& animName) } } +///////////////////////////////////////////////////////////////////////////// +// +// Function : SaveClothingToFile +// PARAMETERS : filename - file to save to +// +// PURPOSE : Save the avatar's clothing to a file. If only a filename is +// given, it will write to UserData/Avatars. +// +bool cyAvatar::SaveClothingToFile(plFileName filename) +{ + if (fRecvr.Count() > 0) { + plArmatureMod* avatar = plAvatarMgr::FindAvatar(fRecvr[0]); + if (avatar) { + plClothingOutfit* cl = avatar->GetClothingOutfit(); + if (cl) { + // Save file in UserData/Avatars if only a filename is given + if (!filename.StripFileName().IsValid()) { + plFileName path = plFileName::Join(plFileSystem::GetUserDataPath(), "Avatars"); + plFileSystem::CreateDir(path, true); + filename = plFileName::Join(path, filename); + } + return cl->WriteToFile(filename); + } + } + } + return false; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Function : LoadClothingFromFile +// PARAMETERS : filename - file to load from +// +// PURPOSE : Load the avatar's clothing from a file. If only a filename is +// given, it will read from UserData/Avatars. +// +bool cyAvatar::LoadClothingFromFile(plFileName filename) +{ + if (fRecvr.Count() > 0) { + plArmatureMod* avatar = plAvatarMgr::FindAvatar(fRecvr[0]); + if (avatar) { + plClothingOutfit* cl = avatar->GetClothingOutfit(); + if (cl) { + // Search for file in UserData/Avatars if only a filename is given + if (!filename.StripFileName().IsValid()) + filename = plFileName::Join(plFileSystem::GetUserDataPath(), "Avatars", filename); + cl->SetClothingFile(filename); + return cl->ReadClothing(); + } + } + } + return false; +} + ///////////////////////////////////////////////////////////////////////////// // // Function : ChangeAvatar diff --git a/Sources/Plasma/FeatureLib/pfPython/cyAvatar.h b/Sources/Plasma/FeatureLib/pfPython/cyAvatar.h index e5610de3..7a7fdcee 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyAvatar.h +++ b/Sources/Plasma/FeatureLib/pfPython/cyAvatar.h @@ -410,6 +410,9 @@ public: virtual void PlaySimpleAnimation(const plString& animName); + virtual bool SaveClothingToFile(plFileName filename); + virtual bool LoadClothingFromFile(plFileName filename); + ///////////////////////////////////////////////////////////////////////////// // // Function : ChangeAvatar diff --git a/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp b/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp index ac8c1e18..9c98993a 100644 --- a/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp +++ b/Sources/Plasma/FeatureLib/pfPython/cyAvatarGlue.cpp @@ -597,6 +597,30 @@ PYTHON_METHOD_DEFINITION(ptAvatar, playSimpleAnimation, args) PYTHON_RETURN_NONE; } +PYTHON_METHOD_DEFINITION(ptAvatar, saveClothingToFile, args) +{ + PyObject* filename; + if (!PyArg_ParseTuple(args, "O", &filename) || !PyString_CheckEx(filename)) + { + PyErr_SetString(PyExc_TypeError, "saveClothingToFile expects a string object"); + PYTHON_RETURN_ERROR; + } + + PYTHON_RETURN_BOOL(self->fThis->SaveClothingToFile(PyString_AsStringEx(filename))); +} + +PYTHON_METHOD_DEFINITION(ptAvatar, loadClothingFromFile, args) +{ + PyObject* filename; + if (!PyArg_ParseTuple(args, "O", &filename) || !PyString_CheckEx(filename)) + { + PyErr_SetString(PyExc_TypeError, "loadClothingFromFile expects a string object"); + PYTHON_RETURN_ERROR; + } + + PYTHON_RETURN_BOOL(self->fThis->LoadClothingFromFile(PyString_AsStringEx(filename))); +} + PYTHON_START_METHODS_TABLE(ptAvatar) PYTHON_METHOD(ptAvatar, netForce, "Params: forceFlag\nSpecify whether this object needs to use messages that are forced to the network\n" "- This is to be used if your Python program is running on only one client\n" @@ -651,6 +675,9 @@ PYTHON_START_METHODS_TABLE(ptAvatar) PYTHON_METHOD(ptAvatar, unRegisterForBehaviorNotify, "Params: selfKey\nThis will unregister behavior notifications"), PYTHON_METHOD(ptAvatar, playSimpleAnimation, "Params: animName\nPlay simple animation on avatar"), + + PYTHON_METHOD(ptAvatar, saveClothingToFile, "Params: filename\nSave avatar clothing to a file"), + PYTHON_METHOD(ptAvatar, loadClothingFromFile, "Params: filename\nLoad avatar clothing from a file"), PYTHON_END_METHODS_TABLE; PYTHON_GLOBAL_METHOD_DEFINITION(PtSetBehaviorLoopCount, args, "Params: behaviorKey,stage,loopCount,netForce\nThis will set the loop count for a particular stage in a multistage behavior") diff --git a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h index a185eedc..4cb01771 100644 --- a/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h +++ b/Sources/Plasma/NucleusLib/inc/plCreatableIndex.h @@ -951,6 +951,7 @@ CLASS_INDEX_LIST_START CLASS_INDEX(pfGameScoreListMsg), CLASS_INDEX(pfGameScoreTransferMsg), CLASS_INDEX(pfGameScoreUpdateMsg), + CLASS_INDEX(plLoadClothingMsg), CLASS_INDEX_LIST_END #endif // plCreatableIndex_inc diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp index 40f37d21..0563cfe8 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.cpp @@ -84,6 +84,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plMessage/plListenerMsg.h" #include "plMessage/plAgeLoadedMsg.h" #include "plMessage/plParticleUpdateMsg.h" +#include "plMessage/plLoadClothingMsg.h" #include "plParticleSystem/plParticleSystem.h" #include "plParticleSystem/plParticleSDLMod.h" @@ -784,29 +785,22 @@ void plArmatureMod::WindowActivate(bool active) } } -char *plArmatureMod::fSpawnPointOverride = nil; +plString plArmatureMod::fSpawnPointOverride; -void plArmatureMod::SetSpawnPointOverride( const char *overrideObjName ) +void plArmatureMod::SetSpawnPointOverride(const plString &overrideObjName) { - delete [] fSpawnPointOverride; - if( overrideObjName == nil ) - fSpawnPointOverride = nil; - else - { - fSpawnPointOverride = hsStrcpy( overrideObjName ); - strlwr( fSpawnPointOverride ); - } + fSpawnPointOverride = overrideObjName.ToLower(); } -int plArmatureMod::IFindSpawnOverride( void ) +int plArmatureMod::IFindSpawnOverride() { - if( fSpawnPointOverride == nil || fSpawnPointOverride[ 0 ] == 0 ) + if (fSpawnPointOverride.IsEmpty()) return -1; - int i; + int i; plAvatarMgr *mgr = plAvatarMgr::GetInstance(); - for( i = 0; i < mgr->NumSpawnPoints(); i++ ) + for (i = 0; i < mgr->NumSpawnPoints(); i++) { - const plString &name = mgr->GetSpawnPoint( i )->GetTarget(0)->GetKeyName(); + const plString &name = mgr->GetSpawnPoint(i)->GetTarget(0)->GetKeyName(); if (name.Find(fSpawnPointOverride, plString::kCaseInsensitive) >= 0) return i; // Found it! } @@ -1300,15 +1294,11 @@ bool plArmatureMod::MsgReceive(plMessage* msg) } } - // copy the user string over - const char* userStr = avLoadMsg->GetUserStr(); - if (userStr) - fUserStr = userStr; - else - fUserStr = ""; + // We also want to use the trigger msg when loading an avatar + MsgReceive(avLoadMsg->GetTriggerMsg()); return true; - } + } plLoadCloneMsg *cloneMsg = plLoadCloneMsg::ConvertNoRef(msg); if (cloneMsg) @@ -1347,6 +1337,15 @@ bool plArmatureMod::MsgReceive(plMessage* msg) } } + plLoadClothingMsg *clothingMsg = plLoadClothingMsg::ConvertNoRef(msg); + if (clothingMsg) + { + // We got a clothing file and are supposed to load our avatar from it. + // Let's tell our outfit to do so! + fClothingOutfit->SetClothingFile(clothingMsg->GetClothingFile()); + return true; + } + plLinkEffectBCMsg *linkBCMsg = plLinkEffectBCMsg::ConvertNoRef(msg); if (linkBCMsg) { diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h index 5fb2cbd8..c15992e0 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plArmatureMod.h @@ -329,7 +329,7 @@ public: static void SetMouseTurnSensitivity(float val) { fMouseTurnSensitivity = val / 150.f; } static float GetMouseTurnSensitivity() { return fMouseTurnSensitivity * 150.f; } - static void SetSpawnPointOverride( const char *overrideObjName ); + static void SetSpawnPointOverride(const plString &overrideObjName); static void WindowActivate(bool active); void SetFollowerParticleSystemSO(plSceneObject *follower); plSceneObject *GetFollowerParticleSystemSO(); @@ -426,7 +426,7 @@ protected: hsTArray fClothToSOMap; plArmatureEffectsMgr *fEffects; plSceneObject *fFollowerParticleSystemSO; - static char *fSpawnPointOverride; + static plString fSpawnPointOverride; // These vectors are used with relevance regions for culling out other objects hsBitVector fRegionsImIn; diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp index 20fc9cc7..a2c0ed97 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvBrainHuman.cpp @@ -193,8 +193,10 @@ void plAvBrainHuman::Activate(plArmatureModBase *avMod) if (fAvMod->GetClothingOutfit() && fAvMod->GetClothingOutfit()->fGroup != plClothingMgr::kClothingBaseNoOptions) { - if (fAvMod->IsLocalAvatar()) - fAvMod->GetClothingOutfit()->ReadFromVault(); + if (fAvMod->IsLocalAvatar()) + { + fAvMod->GetClothingOutfit()->ReadClothing(); + } else { fAvMod->GetClothingOutfit()->WearDefaultClothing(); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp index b1001043..687acd30 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.cpp @@ -430,7 +430,8 @@ bool plClothingBase::MsgReceive(plMessage* msg) ///////////////////////////////////////////////////////////////////////////// plClothingOutfit::plClothingOutfit() : - fTargetLayer(nil), fBase(nil), fGroup(0), fAvatar(nil), fSynchClients(false), fMaterial(nil), fVaultSaveEnabled(true), fMorphsInitDone(false) + fTargetLayer(nullptr), fBase(nullptr), fGroup(0), fAvatar(nullptr), fSynchClients(false), fMaterial(nullptr), + fVaultSaveEnabled(true), fMorphsInitDone(false) { fSkinTint.Set(1.f, 0.84, 0.71, 1.f); fItems.Reset(); @@ -802,15 +803,24 @@ void plClothingOutfit::IHandleMorphSDR(plStateDataRecord *sdr) morph->SetCurrentStateFrom(sdr); } -void plClothingOutfit::ReadFromVault() +bool plClothingOutfit::ReadClothing() +{ + // Have we set a clothing file? If that's the case, load from there. + if (fClothingFile.IsValid()) + return IReadFromFile(fClothingFile); + else + return IReadFromVault(); +} + +bool plClothingOutfit::IReadFromVault() { SetupMorphSDL(); WearDefaultClothing(); - RelVaultNode * rvn; - if (nil == (rvn = VaultGetAvatarOutfitFolderIncRef())) - return; + RelVaultNode * rvn = VaultGetAvatarOutfitFolderIncRef(); + if (!rvn) + return false; ARRAY(RelVaultNode*) nodes; rvn->GetChildNodesIncRef(plVault::kNodeType_SDL, 1, &nodes); @@ -844,6 +854,7 @@ void plClothingOutfit::ReadFromVault() ForceUpdate(true); rvn->DecRef(); + return true; } void plClothingOutfit::SaveCustomizations(bool retry /* = true */) @@ -1135,7 +1146,7 @@ void plClothingOutfit::WearMaintainerOutfit() void plClothingOutfit::RemoveMaintainerOutfit() { - ReadFromVault(); + ReadClothing(); fVaultSaveEnabled = true; } @@ -1482,6 +1493,91 @@ void plClothingOutfit::SetupMorphSDL() } } +bool plClothingOutfit::WriteToFile(const plFileName &filename) +{ + if (!filename.IsValid()) + return false; + + RelVaultNode* rvn = VaultGetAvatarOutfitFolderIncRef(); + if (!rvn) + return false; + + hsUNIXStream S; + if (!S.Open(filename, "wb")) { + rvn->DecRef(); + return false; + } + + S.WriteByte(fGroup); + + ARRAY(RelVaultNode*) nodes; + rvn->GetChildNodesIncRef(plVault::kNodeType_SDL, 1, &nodes); + S.WriteLE32(nodes.Count()); + for (size_t i = 0; i < nodes.Count(); i++) { + VaultSDLNode sdl(nodes[i]); + S.WriteLE32(sdl.GetSDLDataLength()); + if (sdl.GetSDLDataLength()) + S.Write(sdl.GetSDLDataLength(), sdl.GetSDLData()); + nodes[i]->DecRef(); + } + rvn->DecRef(); + + S.Close(); + return true; +} + +bool plClothingOutfit::IReadFromFile(const plFileName &filename) +{ + if (!filename.IsValid()) + return false; + + hsUNIXStream S; + if (!S.Open(filename)) + return false; + + bool isLocalAvatar = plAvatarMgr::GetInstance()->GetLocalAvatar()->GetClothingOutfit() == this; + + uint8_t gender = S.ReadByte(); + if (gender != fGroup) { + if (isLocalAvatar) { + if (gender == plClothingMgr::kClothingBaseMale) + plClothingMgr::ChangeAvatar("Male", filename); + else if (gender == plClothingMgr::kClothingBaseFemale) + plClothingMgr::ChangeAvatar("Female", filename); + } + S.Close(); + return true; + } + + StripAccessories(); + + uint32_t nodeCount = S.ReadLE32(); + for (size_t i = 0; i < nodeCount; i++) { + uint32_t dataLen = S.ReadLE32(); + if (dataLen) { + plString sdlRecName; + int sdlRecVersion; + plStateDataRecord::ReadStreamHeader(&S, &sdlRecName, &sdlRecVersion); + plStateDescriptor* desc = plSDLMgr::GetInstance()->FindDescriptor(sdlRecName, sdlRecVersion); + if (desc) { + plStateDataRecord sdlDataRec(desc); + if (sdlDataRec.Read(&S, 0)) { + if (sdlRecName == kSDLMorphSequence) + IHandleMorphSDR(&sdlDataRec); + else + plClothingSDLModifier::HandleSingleSDR(&sdlDataRec, this); + } + } + } + } + + S.Close(); + fSynchClients = true; + ForceUpdate(true); + SaveCustomizations(); // Sync with the vault + return true; +} + ///////////////////////////////////////////////////////////////////////////// @@ -1832,9 +1928,8 @@ void plClothingMgr::IAddItem(plClothingItem *item) hsAssert(false, "Couldn't match all elements of added clothing item."); } -void plClothingMgr::ChangeAvatar(char *name) +void plClothingMgr::ChangeAvatar(const char* name, const plFileName &clothingFile) { plAvatarMgr::GetInstance()->UnLoadLocalPlayer(); - plAvatarMgr::GetInstance()->LoadPlayer(name, nil); + plAvatarMgr::GetInstance()->LoadPlayerFromFile(name, "", clothingFile); } - diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h index 94c22927..d99595a6 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarClothing.h @@ -215,25 +215,49 @@ public: void IInstanceSharedMeshes(plClothingItem *item); void IRemoveSharedMeshes(plClothingItem *item); - void ReadFromVault(); + /** This will load the avatar clothing. If a clothing file is set, + * we will load from the file, otherwise from the vault. + */ + bool ReadClothing(); + void WriteToVault(); void WriteToVault(const ARRAY(plStateDataRecord*) & SDRs); + /** Write the avatar clothing to a file */ + bool WriteToFile(const plFileName &filename); + void SetupMorphSDL(); // XXX Don't use this. Temp function for a temp HACK console command. void DirtyTileset(int tileset); + /** Instruct this plClothingOutfit to read clothing from the given file */ + void SetClothingFile(const plFileName &file) { fClothingFile = file; } + + /** Returns the clothing file of this outfit. If there is none, an empty string + * will be returned. + */ + plFileName GetClothingFile() const { return fClothingFile; } + protected: hsBitVector fDirtyItems; bool fVaultSaveEnabled; bool fMorphsInitDone; + plFileName fClothingFile; void IAddItem(plClothingItem *item); void IRemoveItem(plClothingItem *item); bool ITintItem(plClothingItem *item, hsColorRGBA color, uint8_t layer); bool IMorphItem(plClothingItem *item, uint8_t layer, uint8_t delta, float weight); void IHandleMorphSDR(plStateDataRecord *sdr); + + bool IReadFromVault(); + + /** Read the avatar clothing from a file. + * A local avatar will change the clothing group to the one in the file. + * A local avatar will be invisible if the file does not exist. (used in the Startup age) + */ + bool IReadFromFile(const plFileName &filename); void IUpdate(); @@ -281,7 +305,7 @@ public: plClothingItem *GetLRMatch(plClothingItem *item); bool IsLRMatch(plClothingItem *item1, plClothingItem *item2); - static void ChangeAvatar(char *name); + static void ChangeAvatar(const char* name, const plFileName &clothingFile = ""); static plClothingMgr *GetClothingMgr() { return fInstance; } static void Init(); diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.cpp b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.cpp index 7498d31e..2ebe613b 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.cpp @@ -83,6 +83,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "plMessage/plMemberUpdateMsg.h" #include "plMessage/plAvatarMsg.h" #include "plMessage/plAvCoopMsg.h" +#include "plMessage/plLoadClothingMsg.h" #include "pnMessage/plTimeMsg.h" #include "plStatusLog/plStatusLog.h" @@ -149,12 +150,12 @@ void plAvatarMgr::IReset() fActiveCoops.clear(); } -plKey plAvatarMgr::LoadPlayer(const char *name, const char *account) +plKey plAvatarMgr::LoadPlayer(const plString &name, const plString &account) { - return LoadAvatar(name, account, true, nil, nil); + return LoadAvatar(name, account, true, nullptr, nullptr); } -plKey plAvatarMgr::LoadPlayer(const char *name, const char *account, const char *linkInName) +plKey plAvatarMgr::LoadPlayer(const plString &name, const plString &account, const plString &linkInName) { // what we'd like to do is turn the linkInName into a spawn point key and // put that into the plLoadAvatarMsg, which is already set up to handle @@ -163,29 +164,34 @@ plKey plAvatarMgr::LoadPlayer(const char *name, const char *account, const char // so we're goin to do this the "old way" for now. plArmatureMod::SetSpawnPointOverride(linkInName); - return LoadAvatar(name, account, true, nil, nil); + return LoadAvatar(name, account, true, nullptr, nullptr); } +plKey plAvatarMgr::LoadPlayerFromFile(const plString &name, const plString &account, const plFileName &clothingFile) +{ + return LoadAvatar(name, account, true, nullptr, nullptr, "", clothingFile); +} -plKey plAvatarMgr::LoadAvatar(const char *name, const char *accountName, bool isPlayer, plKey spawnPoint, plAvTask *initialTask, const char *userStr /*=nil*/) +plKey plAvatarMgr::LoadAvatar(plString name, plString accountName, bool isPlayer, plKey spawnPoint, plAvTask *initialTask, + const plString &userStr, const plFileName &clothingFile) { // *** account is currently unused. the idea is that eventually an NPC will // *** be able to use a customization account - plKey result = nil; + plKey result = nullptr; plKey requestor = GetKey(); // avatar manager is always the requestor for avatar loads plNetClientMgr *netMgr = plNetClientMgr::GetInstance(); if(netMgr) // can't clone without the net manager { - hsAssert(name, "name required by LoadPlayer fxn"); - netMgr->DebugMsg("Local: Loading player %s", name); + hsAssert(!name.IsEmpty(), "name required by LoadPlayer fxn"); + netMgr->DebugMsg("Local: Loading player %s", name.c_str()); // look up player by key name provided by user. // this string search should be replaced with some other method of // avatar selection and key lookup. // Get the location for the player first - plKey playerKey = nil; + plKey playerKey = nullptr; const plLocation& globalLoc = plKeyFinder::Instance().FindLocation("GlobalAvatars", name); const plLocation& maleLoc = plKeyFinder::Instance().FindLocation("GlobalAvatars", "Male"); const plLocation& custLoc = plKeyFinder::Instance().FindLocation("CustomAvatars", name); @@ -199,14 +205,18 @@ plKey plAvatarMgr::LoadAvatar(const char *name, const char *accountName, bool is const plLocation& loc = (globalLoc.IsValid() ? globalLoc : custLoc.IsValid() ? custLoc : maleLoc); #endif - plString theName = name; - if ( loc == maleLoc ) - theName = "Male"; + if (loc == maleLoc) + name = "Male"; if (loc.IsValid()) { - plUoid uID(loc, plSceneObject::Index(), theName); - plLoadAvatarMsg *cloneMsg = new plLoadAvatarMsg (uID, requestor, 0, isPlayer, spawnPoint, initialTask, userStr); + plUoid uID(loc, plSceneObject::Index(), name); + plLoadAvatarMsg *cloneMsg = new plLoadAvatarMsg(uID, requestor, 0, isPlayer, spawnPoint, initialTask, userStr); + if (clothingFile.IsValid()) + { + plLoadClothingMsg *clothingMsg = new plLoadClothingMsg(clothingFile); + cloneMsg->SetTriggerMsg(clothingMsg); + } result = cloneMsg->GetCloneKey(); // the clone message is automatically addressed to the net client manager diff --git a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.h b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.h index 5aed7bab..7fad8851 100644 --- a/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.h +++ b/Sources/Plasma/PubUtilLib/plAvatar/plAvatarMgr.h @@ -48,6 +48,7 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com #include "HeadSpin.h" #include +#include "plFileSystem.h" #include "hsGeometry3.h" #include "pnKeyedObject/hsKeyedObject.h" @@ -113,9 +114,11 @@ public: plOneShotMod *FindOneShot(const plString &name); // \} - plKey LoadPlayer(const char* name, const char *account); - plKey LoadPlayer(const char* name, const char *account, const char *linkName); - plKey LoadAvatar(const char *name, const char *accountName, bool isPlayer, plKey spawnPoint, plAvTask *initialTask, const char *userStr = nil); + plKey LoadPlayer(const plString &name, const plString &account); + plKey LoadPlayer(const plString &name, const plString &account, const plString &linkName); + plKey LoadPlayerFromFile(const plString &name, const plString &account, const plFileName &clothingFile); + plKey LoadAvatar(plString name, plString accountName, bool isPlayer, plKey spawnPoint, plAvTask *initialTask, + const plString &userStr = "", const plFileName &clothingFile = ""); /** * Unload an avatar clone diff --git a/Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt b/Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt index 077f347f..848f0fbc 100644 --- a/Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt +++ b/Sources/Plasma/PubUtilLib/plMessage/CMakeLists.txt @@ -80,6 +80,7 @@ set(plMessage_HEADERS plLoadAgeMsg.h plLoadAvatarMsg.h plLoadCloneMsg.h + plLoadClothingMsg.h plLOSHitMsg.h plLOSRequestMsg.h plMatRefMsg.h diff --git a/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.cpp b/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.cpp index 95b58436..3c31673d 100644 --- a/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.cpp +++ b/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.cpp @@ -58,44 +58,30 @@ You can contact Cyan Worlds, Inc. by email legal@cyan.com // CTOR (default) plLoadAvatarMsg::plLoadAvatarMsg() : fIsPlayer(false), - fSpawnPoint(nil), - fInitialTask(nil), - fUserStr(nil) + fSpawnPoint(nullptr), + fInitialTask(nullptr) { } // CTOR uoidToClone, requestorKey, userData, isPlayer, spawnPOint, initialTask plLoadAvatarMsg::plLoadAvatarMsg(const plUoid &uoidToClone, const plKey &requestorKey, uint32_t userData, - bool isPlayer, const plKey &spawnPoint, plAvTask *initialTask, const char* userStr /*= nil*/) + bool isPlayer, const plKey &spawnPoint, plAvTask *initialTask, const plString &userStr) : plLoadCloneMsg(uoidToClone, requestorKey, userData), fIsPlayer(isPlayer), fSpawnPoint(spawnPoint), fInitialTask(initialTask), - fUserStr(nil) // setting to nil so SetUserStr doesn't try to nuke garbage + fUserStr(userStr) { - SetUserStr(userStr); } plLoadAvatarMsg::plLoadAvatarMsg(const plKey &existing, const plKey &requestor, uint32_t userData, - bool isPlayer, bool isLoading, const char* userStr /*= nil*/) -: plLoadCloneMsg(existing, requestor, userData, isLoading), - fIsPlayer(isPlayer), - fSpawnPoint(nil), - fInitialTask(nil), - fUserStr(nil) // setting to nil so SetUserStr doesn't try to nuke garbage -{ - SetUserStr(userStr); -} - - -// DTOR -plLoadAvatarMsg::~plLoadAvatarMsg() + bool isPlayer, bool isLoading, const plString &userStr) +: plLoadCloneMsg(existing, requestor, userData, isLoading), + fIsPlayer(isPlayer), + fSpawnPoint(nullptr), + fInitialTask(nullptr), + fUserStr(userStr) { - if (fUserStr) - { - delete [] fUserStr; - fUserStr = nil; - } } void plLoadAvatarMsg::Read(hsStream* stream, hsResMgr* mgr) @@ -107,12 +93,7 @@ void plLoadAvatarMsg::Read(hsStream* stream, hsResMgr* mgr) { fInitialTask = plAvTask::ConvertNoRef(mgr->ReadCreatable(stream)); } - if (fUserStr) - { - delete [] fUserStr; - fUserStr = nil; - } - fUserStr = stream->ReadSafeString(); + fUserStr = stream->ReadSafeString_TEMP(); } void plLoadAvatarMsg::Write(hsStream *stream, hsResMgr *mgr) @@ -150,13 +131,8 @@ void plLoadAvatarMsg::ReadVersion(hsStream* stream, hsResMgr* mgr) if (contentFlags.IsBitSet(kLoadAvatarMsgSpawnPoint)) fSpawnPoint = mgr->ReadKey(stream); - if (fUserStr) - { - delete [] fUserStr; - fUserStr = nil; - } if (contentFlags.IsBitSet(kLoadAvatarMsgUserStr)) - fUserStr = stream->ReadSafeString(); + fUserStr = stream->ReadSafeString_TEMP(); } void plLoadAvatarMsg::WriteVersion(hsStream* stream, hsResMgr* mgr) @@ -179,63 +155,5 @@ void plLoadAvatarMsg::WriteVersion(hsStream* stream, hsResMgr* mgr) stream->WriteSafeString(fUserStr); } -// SETISPLAYER -void plLoadAvatarMsg::SetIsPlayer(bool is) -{ - fIsPlayer = is; -} - -// GETISPLAYER -bool plLoadAvatarMsg::GetIsPlayer() -{ - return fIsPlayer; -} - -// SETSPAWNPOINT -void plLoadAvatarMsg::SetSpawnPoint(const plKey &spawnPoint) -{ - fSpawnPoint = spawnPoint; -} - -// GETSPAWNPOINT -plKey plLoadAvatarMsg::GetSpawnPoint() -{ - return fSpawnPoint; -} - -// SETINITIALTASK -void plLoadAvatarMsg::SetInitialTask(plAvTask *initialTask) -{ - fInitialTask = initialTask; -} - -// GETINITIALTASK -plAvTask * plLoadAvatarMsg::GetInitialTask() -{ - return fInitialTask; -} - -// SETUSERSTR -void plLoadAvatarMsg::SetUserStr(const char *userStr) -{ - if (fUserStr) - delete [] fUserStr; - if (!userStr) - { - fUserStr = nil; - return; - } - - fUserStr = new char[strlen(userStr) + 1]; - strcpy(fUserStr, userStr); - fUserStr[strlen(userStr)] = '\0'; -} - -// GETUSERSTR -const char* plLoadAvatarMsg::GetUserStr() -{ - return fUserStr; -} - #endif // ndef SERVER #endif // ndef NO_AV_MSGS diff --git a/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.h b/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.h index 973546af..833103db 100644 --- a/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.h +++ b/Sources/Plasma/PubUtilLib/plMessage/plLoadAvatarMsg.h @@ -83,7 +83,7 @@ public: \param userStr - a string that the user can set */ plLoadAvatarMsg(const plUoid &uoidToClone, const plKey &requestorKey, uint32_t userData, - bool isPlayer, const plKey &spawnPoint, plAvTask *initialTask, const char *userStr = nil); + bool isPlayer, const plKey &spawnPoint, plAvTask *initialTask, const plString &userStr = ""); /** Use this form if you're sending a message about an existing clone -- either to propagate it to other machines or to tell them to unload it. @@ -97,21 +97,19 @@ public: \param userStr - a string that the user can set */ plLoadAvatarMsg(const plKey &existing, const plKey &requestorKey, uint32_t userData, - bool isPlayer, bool isLoading, const char *userStr = nil); + bool isPlayer, bool isLoading, const plString &userStr = ""); - virtual ~plLoadAvatarMsg(); + void SetIsPlayer(bool is) { fIsPlayer = is; } + bool GetIsPlayer() { return fIsPlayer; } - void SetIsPlayer(bool is); - bool GetIsPlayer(); + void SetSpawnPoint(const plKey &spawnPoint) { fSpawnPoint = spawnPoint; } + plKey GetSpawnPoint() { return fSpawnPoint; } - void SetSpawnPoint(const plKey &spawnSceneObjectKey); - plKey GetSpawnPoint(); + void SetInitialTask(plAvTask *task) { fInitialTask = task; } + plAvTask * GetInitialTask() { return fInitialTask; } - void SetInitialTask(plAvTask *task); - plAvTask * GetInitialTask(); - - void SetUserStr(const char *userStr); - const char* GetUserStr(); + void SetUserStr(const plString &userStr) { fUserStr = userStr; } + plString GetUserStr() { return fUserStr; } CLASSNAME_REGISTER(plLoadAvatarMsg); GETINTERFACE_ANY(plLoadAvatarMsg, plLoadCloneMsg); @@ -126,7 +124,7 @@ protected: bool fIsPlayer; plKey fSpawnPoint; plAvTask *fInitialTask; - char *fUserStr; + plString fUserStr; }; diff --git a/Sources/Plasma/PubUtilLib/plMessage/plLoadClothingMsg.h b/Sources/Plasma/PubUtilLib/plMessage/plLoadClothingMsg.h new file mode 100644 index 00000000..cab22ede --- /dev/null +++ b/Sources/Plasma/PubUtilLib/plMessage/plLoadClothingMsg.h @@ -0,0 +1,67 @@ +/*==LICENSE==* + +CyanWorlds.com Engine - MMOG client, server and tools +Copyright (C) 2011 Cyan Worlds, Inc. + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +Additional permissions under GNU GPL version 3 section 7 + +If you modify this Program, or any covered work, by linking or +combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK, +NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent +JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK +(or a modified version of those libraries), +containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA, +PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG +JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the +licensors of this Program grant you additional +permission to convey the resulting work. Corresponding Source for a +non-source form of such a combination shall include the source code for +the parts of OpenSSL and IJG JPEG Library used as well as that of the covered +work. + +You can contact Cyan Worlds, Inc. by email legal@cyan.com + or by snail mail at: + Cyan Worlds, Inc. + 14617 N Newport Hwy + Mead, WA 99021 + +*==LICENSE==*/ + +#ifndef plLoadClothingMsg_INC +#define plLoadClothingMsg_INC + +#include "plFileSystem.h" +#include "pnMessage/plMessage.h" + +/** This message is sent when we want to load our clothing from a file. */ +class plLoadClothingMsg : public plMessage { +private: + plFileName fClothingFile; + +public: + plLoadClothingMsg() { } + plLoadClothingMsg(const plFileName& file) : fClothingFile(file) { } + + CLASSNAME_REGISTER(plLoadClothingMsg); + GETINTERFACE_ANY(plLoadClothingMsg, plMessage); + + void Read(hsStream*, hsResMgr*) { } + void Write(hsStream*, hsResMgr*) { } + + plFileName GetClothingFile() const { return fClothingFile; } +}; + +#endif // plLoadClothingMsg_INC diff --git a/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h b/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h index 812f998a..855e23cc 100644 --- a/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h +++ b/Sources/Plasma/PubUtilLib/plMessage/plMessageCreatable.h @@ -355,6 +355,9 @@ REGISTER_CREATABLE(plLoadCloneMsg); # include "plLoadAvatarMsg.h" REGISTER_CREATABLE(plLoadAvatarMsg); +#include "plLoadClothingMsg.h" +REGISTER_CREATABLE(plLoadClothingMsg); + # include "plAvCoopMsg.h" REGISTER_CREATABLE(plAvCoopMsg); diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp index 197b4e65..87c2a432 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetCliAgeJoiner.cpp @@ -281,7 +281,7 @@ void plNCAgeJoiner::ExecNextOp () { else avatarName = NetCommGetPlayer()->avatarDatasetName; plString linkInName = plNetLinkingMgr::GetInstance()->GetAgeLink()->SpawnPoint().GetName(); - am->LoadPlayer( avatarName, nil, linkInName.c_str() ); + am->LoadPlayer(avatarName, "", linkInName); } else { LogMsg(kLogPerf, L"AgeJoiner: Next:kPropagatePlayer"); diff --git a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp index e536afd0..db7825ca 100644 --- a/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp +++ b/Sources/Plasma/PubUtilLib/plNetClient/plNetClientMgr.cpp @@ -198,9 +198,6 @@ void plNetClientMgr::Shutdown() IRemoveCloneRoom(); - // RATHER BAD DEBUG HACK: Clear the spawn override in armatureMod so there's no memory leak - plArmatureMod::SetSpawnPointOverride( nil ); - VaultDestroy(); }