[General] Implement RecordDynamic packet, part 1

Spell, potion, enchantment, creature, NPC, armor, book, clothing, miscellaneous and weapon record data can now be sent in a RecordDynamic packet. Additionally, the packets include data related to associated magical effects (for spells, potions and enchantments), data related to default inventory contents (for creatures and NPCs) and data related to body parts affected (for armor and clothing).

The server now has associated script functions for setting most of the details of the above, with the main exception being individual creature and NPC stats.

Records can either be created entirely from scratch or can use an existing record (set via the baseId variable) as a starting point for their values. In the latter case, only the values that are specifically set override the starting values. Creature and NPC records also have an inventoryBaseId that can be used on top of the baseId to base their inventories on another existing record.

The client's RecordHelper class has been heavily expanded to allow for the above mentioned functionality.

When players create spells, potions and enchantments as part of regular gameplay, they send RecordDynamic packets that provide the server with the complete details of the records that should be created. When they create enchantments, they also provide the server with armor, book, clothing and weapon records corresponding to the items they've enchanted.

This functionality added by this packet was originally supposed to be exclusive to the rewrite, but I've gone ahead and tried to provide it for the pre-rewrite in a way that can mostly be reused for the rewrite.
pull/471/head
David Cernat 6 years ago
parent 2dbf3893c0
commit b57807407a

@ -64,9 +64,9 @@ set(SERVER
Script/Functions/Books.cpp Script/Functions/Cells.cpp Script/Functions/CharClass.cpp
Script/Functions/Chat.cpp Script/Functions/Dialogue.cpp Script/Functions/Factions.cpp
Script/Functions/GUI.cpp Script/Functions/Items.cpp Script/Functions/Mechanics.cpp
Script/Functions/Positions.cpp Script/Functions/Quests.cpp Script/Functions/Server.cpp
Script/Functions/Settings.cpp Script/Functions/Shapeshift.cpp Script/Functions/Spells.cpp
Script/Functions/Stats.cpp Script/Functions/Timer.cpp
Script/Functions/Positions.cpp Script/Functions/Quests.cpp Script/Functions/RecordsDynamic.cpp
Script/Functions/Server.cpp Script/Functions/Settings.cpp Script/Functions/Shapeshift.cpp
Script/Functions/Spells.cpp Script/Functions/Stats.cpp Script/Functions/Timer.cpp
Script/API/TimerAPI.cpp Script/API/PublicFnAPI.cpp
${LuaScript_Sources}

@ -122,7 +122,7 @@ public:
static void ReadCellActorList(const char* cellDescription) noexcept;
/**
* \brief Clear the data from the last actor list sent by the server.
* \brief Clear the data from the actor list stored on the server.
*
* \return void
*/

File diff suppressed because it is too large Load Diff

@ -0,0 +1,880 @@
#ifndef OPENMW_RECORDSDYNAMICAPI_HPP
#define OPENMW_RECORDSDYNAMICAPI_HPP
#include "../Types.hpp"
#define RECORDSDYNAMICAPI \
{"ClearRecords", RecordsDynamicFunctions::ClearRecords},\
\
{"GetRecordType", RecordsDynamicFunctions::GetRecordType},\
{"GetRecordCount", RecordsDynamicFunctions::GetRecordCount},\
{"GetRecordEffectCount", RecordsDynamicFunctions::GetRecordEffectCount},\
\
{"GetRecordId", RecordsDynamicFunctions::GetRecordId},\
{"GetRecordBaseId", RecordsDynamicFunctions::GetRecordBaseId},\
\
{"GetRecordSubtype", RecordsDynamicFunctions::GetRecordSubtype},\
{"GetRecordName", RecordsDynamicFunctions::GetRecordName},\
{"GetRecordModel", RecordsDynamicFunctions::GetRecordModel},\
{"GetRecordIcon", RecordsDynamicFunctions::GetRecordIcon},\
{"GetRecordScript", RecordsDynamicFunctions::GetRecordScript},\
{"GetRecordEnchantmentId", RecordsDynamicFunctions::GetRecordEnchantmentId},\
{"GetRecordEnchantmentCharge", RecordsDynamicFunctions::GetRecordEnchantmentCharge},\
\
{"GetRecordAutoCalc", RecordsDynamicFunctions::GetRecordAutoCalc},\
{"GetRecordCharge", RecordsDynamicFunctions::GetRecordCharge},\
{"GetRecordCost", RecordsDynamicFunctions::GetRecordCost},\
{"GetRecordFlags", RecordsDynamicFunctions::GetRecordFlags},\
{"GetRecordValue", RecordsDynamicFunctions::GetRecordValue},\
{"GetRecordWeight", RecordsDynamicFunctions::GetRecordWeight},\
\
{"GetRecordEffectId", RecordsDynamicFunctions::GetRecordEffectId},\
{"GetRecordEffectAttribute", RecordsDynamicFunctions::GetRecordEffectAttribute},\
{"GetRecordEffectSkill", RecordsDynamicFunctions::GetRecordEffectSkill},\
{"GetRecordEffectRangeType", RecordsDynamicFunctions::GetRecordEffectRangeType},\
{"GetRecordEffectArea", RecordsDynamicFunctions::GetRecordEffectArea},\
{"GetRecordEffectDuration", RecordsDynamicFunctions::GetRecordEffectDuration},\
{"GetRecordEffectMagnitudeMax", RecordsDynamicFunctions::GetRecordEffectMagnitudeMax},\
{"GetRecordEffectMagnitudeMin", RecordsDynamicFunctions::GetRecordEffectMagnitudeMin},\
\
{"SetRecordType", RecordsDynamicFunctions::SetRecordType},\
\
{"SetRecordId", RecordsDynamicFunctions::SetRecordId},\
{"SetRecordBaseId", RecordsDynamicFunctions::SetRecordBaseId},\
{"SetRecordInventoryBaseId", RecordsDynamicFunctions::SetRecordInventoryBaseId},\
\
{"SetRecordSubtype", RecordsDynamicFunctions::SetRecordSubtype},\
{"SetRecordName", RecordsDynamicFunctions::SetRecordName},\
{"SetRecordModel", RecordsDynamicFunctions::SetRecordModel},\
{"SetRecordIcon", RecordsDynamicFunctions::SetRecordIcon},\
{"SetRecordScript", RecordsDynamicFunctions::SetRecordScript},\
{"SetRecordEnchantmentId", RecordsDynamicFunctions::SetRecordEnchantmentId},\
{"SetRecordEnchantmentCharge", RecordsDynamicFunctions::SetRecordEnchantmentCharge},\
\
{"SetRecordAutoCalc", RecordsDynamicFunctions::SetRecordAutoCalc},\
{"SetRecordCharge", RecordsDynamicFunctions::SetRecordCharge},\
{"SetRecordCost", RecordsDynamicFunctions::SetRecordCost},\
{"SetRecordFlags", RecordsDynamicFunctions::SetRecordFlags},\
{"SetRecordValue", RecordsDynamicFunctions::SetRecordValue},\
{"SetRecordWeight", RecordsDynamicFunctions::SetRecordWeight},\
\
{"SetRecordArmorRating", RecordsDynamicFunctions::SetRecordArmorRating},\
{"SetRecordHealth", RecordsDynamicFunctions::SetRecordHealth},\
\
{"SetRecordDamageChop", RecordsDynamicFunctions::SetRecordDamageChop},\
{"SetRecordDamageSlash", RecordsDynamicFunctions::SetRecordDamageSlash},\
{"SetRecordDamageThrust", RecordsDynamicFunctions::SetRecordDamageThrust},\
{"SetRecordReach", RecordsDynamicFunctions::SetRecordReach},\
{"SetRecordSpeed", RecordsDynamicFunctions::SetRecordSpeed},\
\
{"SetRecordKeyState", RecordsDynamicFunctions::SetRecordKeyState},\
{"SetRecordScrollState", RecordsDynamicFunctions::SetRecordScrollState},\
{"SetRecordSkillId", RecordsDynamicFunctions::SetRecordSkillId},\
{"SetRecordText", RecordsDynamicFunctions::SetRecordText},\
\
{"SetRecordHair", RecordsDynamicFunctions::SetRecordHair},\
{"SetRecordHead", RecordsDynamicFunctions::SetRecordHead},\
{"SetRecordGender", RecordsDynamicFunctions::SetRecordGender},\
{"SetRecordRace", RecordsDynamicFunctions::SetRecordRace},\
{"SetRecordClass", RecordsDynamicFunctions::SetRecordClass},\
{"SetRecordFaction", RecordsDynamicFunctions::SetRecordFaction},\
\
{"SetRecordLevel", RecordsDynamicFunctions::SetRecordLevel},\
\
{"SetRecordIdByIndex", RecordsDynamicFunctions::SetRecordIdByIndex},\
{"SetRecordEnchantmentIdByIndex", RecordsDynamicFunctions::SetRecordEnchantmentIdByIndex},\
\
{"SetRecordEffectId", RecordsDynamicFunctions::SetRecordEffectId},\
{"SetRecordEffectAttribute", RecordsDynamicFunctions::SetRecordEffectAttribute},\
{"SetRecordEffectSkill", RecordsDynamicFunctions::SetRecordEffectSkill},\
{"SetRecordEffectRangeType", RecordsDynamicFunctions::SetRecordEffectRangeType},\
{"SetRecordEffectArea", RecordsDynamicFunctions::SetRecordEffectArea},\
{"SetRecordEffectDuration", RecordsDynamicFunctions::SetRecordEffectDuration},\
{"SetRecordEffectMagnitudeMax", RecordsDynamicFunctions::SetRecordEffectMagnitudeMax},\
{"SetRecordEffectMagnitudeMin", RecordsDynamicFunctions::SetRecordEffectMagnitudeMin},\
\
{"SetRecordBodyPartType", RecordsDynamicFunctions::SetRecordBodyPartType},\
{"SetRecordBodyPartIdForMale", RecordsDynamicFunctions::SetRecordBodyPartIdForMale},\
{"SetRecordBodyPartIdForFemale", RecordsDynamicFunctions::SetRecordBodyPartIdForFemale},\
\
{"SetRecordInventoryItemId", RecordsDynamicFunctions::SetRecordInventoryItemId},\
{"SetRecordInventoryItemCount", RecordsDynamicFunctions::SetRecordInventoryItemCount},\
\
{"AddRecord", RecordsDynamicFunctions::AddRecord},\
{"AddRecordEffect", RecordsDynamicFunctions::AddRecordEffect},\
{"AddRecordBodyPart", RecordsDynamicFunctions::AddRecordBodyPart},\
{"AddRecordInventoryItem", RecordsDynamicFunctions::AddRecordInventoryItem},\
\
{"SendRecordDynamic", RecordsDynamicFunctions::SendRecordDynamic}
class RecordsDynamicFunctions
{
public:
/**
* \brief Clear the data from the records stored on the server.
*
* \return void
*/
static void ClearRecords() noexcept;
/**
* \brief Get the type of records in the read worldstate's dynamic records.
*
* \return The type of records (0 for SPELL, 1 for POTION, 2 for ENCHANTMENT,
* 3 for NPC).
*/
static unsigned short GetRecordType() noexcept;
/**
* \brief Get the number of records in the read worldstate's dynamic records.
*
* \return The number of records.
*/
static unsigned int GetRecordCount() noexcept;
/**
* \brief Get the number of effects for the record at a certain index in the read
* worldstate's current records.
*
* \param recordIndex The index of the record.
* \return The number of effects.
*/
static unsigned int GetRecordEffectCount(unsigned int recordIndex) noexcept;
/**
* \brief Get the id of the record at a certain index in the read worldstate's
* dynamic records of the current type.
*
* \param index The index of the record.
* \return The id of the record.
*/
static const char *GetRecordId(unsigned int index) noexcept;
/**
* \brief Get the base id (i.e. the id this record should inherit default
* values from) of the record at a certain index in the read worldstate's
* dynamic records of the current type.
*
* \param index The index of the record.
* \return The base id of the record.
*/
static const char *GetRecordBaseId(unsigned int index) noexcept;
/**
* \brief Get the subtype of the record at a certain index in the read worldstate's
* dynamic records of the current type.
*
* \param index The index of the record.
* \return The type of the record.
*/
static int GetRecordSubtype(unsigned int index) noexcept;
/**
* \brief Get the name of the record at a certain index in the read worldstate's
* dynamic records of the current type.
*
* \param index The index of the record.
* \return The name of the record.
*/
static const char *GetRecordName(unsigned int index) noexcept;
/**
* \brief Get the model of the record at a certain index in the read worldstate's
* dynamic records of the current type.
*
* \param index The index of the record.
* \return The model of the record.
*/
static const char *GetRecordModel(unsigned int index) noexcept;
/**
* \brief Get the icon of the record at a certain index in the read worldstate's
* dynamic records of the current type.
*
* \param index The index of the record.
* \return The icon of the record.
*/
static const char *GetRecordIcon(unsigned int index) noexcept;
/**
* \brief Get the script of the record at a certain index in the read worldstate's
* dynamic records of the current type.
*
* \param index The index of the record.
* \return The script of the record.
*/
static const char *GetRecordScript(unsigned int index) noexcept;
/**
* \brief Get the enchantment id of the record at a certain index in the read
* worldstate's dynamic records of the current type.
*
* \param index The index of the record.
* \return The enchantment id of the record.
*/
static const char *GetRecordEnchantmentId(unsigned int index) noexcept;
/**
* \brief Get the enchantment charge of the record at a certain index in
* the read worldstate's dynamic records of the current type.
*
* \param index The index of the record.
* \return The enchantment charge of the record.
*/
static int GetRecordEnchantmentCharge(unsigned int index) noexcept;
/**
* \brief Get the auto-calculation flag value of the record at a certain index in
* the read worldstate's dynamic records of the current type.
*
* \param index The index of the record.
* \return The auto-calculation flag value of the record.
*/
static int GetRecordAutoCalc(unsigned int index) noexcept;
/**
* \brief Get the charge of the record at a certain index in the read worldstate's
* dynamic records of the current type.
*
* \param index The index of the record.
* \return The charge of the record.
*/
static int GetRecordCharge(unsigned int index) noexcept;
/**
* \brief Get the cost of the record at a certain index in the read worldstate's
* dynamic records of the current type.
*
* \param index The index of the record.
* \return The cost of the record.
*/
static int GetRecordCost(unsigned int index) noexcept;
/**
* \brief Get the flags of the record at a certain index in the read worldstate's
* dynamic records of the current type.
*
* \param index The index of the record.
* \return The flags of the spell as an integer.
*/
static int GetRecordFlags(unsigned int index) noexcept;
/**
* \brief Get the value of the record at a certain index in the read worldstate's
* dynamic records of the current type.
*
* \param index The index of the record.
* \return The value of the record.
*/
static int GetRecordValue(unsigned int index) noexcept;
/**
* \brief Get the weight of the record at a certain index in the read worldstate's
* dynamic records of the current type.
*
* \param index The index of the record.
* \return The weight of the record.
*/
static double GetRecordWeight(unsigned int index) noexcept;
/**
* \brief Get the ID of the effect at a certain index in the read worldstate's
* current records.
*
* \param recordIndex The index of the record.
* \param effectIndex The index of the effect.
* \return The ID of the effect.
*/
static unsigned int GetRecordEffectId(unsigned int recordIndex, unsigned int effectIndex) noexcept;
/**
* \brief Get the ID of the attribute modified by the effect at a certain index in the
* read worldstate's current records.
*
* \param recordIndex The index of the record.
* \param effectIndex The index of the effect.
* \return The attribute ID for the effect.
*/
static int GetRecordEffectAttribute(unsigned int recordIndex, unsigned int effectIndex) noexcept;
/**
* \brief Get the ID of the skill modified by the effect at a certain index in the
* read worldstate's current records.
*
* \param recordIndex The index of the record.
* \param effectIndex The index of the effect.
* \return The skill ID for the effect.
*/
static int GetRecordEffectSkill(unsigned int recordIndex, unsigned int effectIndex) noexcept;
/**
* \brief Get the range type of the effect at a certain index in the read worldstate's
* current records (0 for self, 1 for touch, 2 for target).
*
* \param recordIndex The index of the record.
* \param effectIndex The index of the effect.
* \return The range of the effect.
*/
static unsigned int GetRecordEffectRangeType(unsigned int recordIndex, unsigned int effectIndex) noexcept;
/**
* \brief Get the area of the effect at a certain index in the read worldstate's current
* records.
*
* \param recordIndex The index of the record.
* \param effectIndex The index of the effect.
* \return The area of the effect.
*/
static int GetRecordEffectArea(unsigned int recordIndex, unsigned int effectIndex) noexcept;
/**
* \brief Get the duration of the effect at a certain index in the read worldstate's current
* records.
*
* \param recordIndex The index of the record.
* \param effectIndex The index of the effect.
* \return The duration of the effect.
*/
static int GetRecordEffectDuration(unsigned int recordIndex, unsigned int effectIndex) noexcept;
/**
* \brief Get the maximum magnitude of the effect at a certain index in the read
* worldstate's current records.
*
* \param recordIndex The index of the record.
* \param effectIndex The index of the effect.
* \return The maximum magnitude of the effect.
*/
static int GetRecordEffectMagnitudeMax(unsigned int recordIndex, unsigned int effectIndex) noexcept;
/**
* \brief Get the minimum magnitude of the effect at a certain index in the read
* worldstate's current records.
*
* \param recordIndex The index of the record.
* \param effectIndex The index of the effect.
* \return The minimum magnitude of the effect.
*/
static int GetRecordEffectMagnitudeMin(unsigned int recordIndex, unsigned int effectIndex) noexcept;
/**
* \brief Set which type of temporary records stored on the server should have
* their data changed via setter functions.
*
* \param type The type of records.
* \return void
*/
static void SetRecordType(unsigned int type) noexcept;
/**
* \brief Set the id of the temporary record stored on the server for the
* currently specified record type.
*
* \param id The id of the record.
* \return void
*/
static void SetRecordId(const char* id) noexcept;
/**
* \brief Set the base id (i.e. the id this record should inherit default
* values from) of the temporary record stored on the server for the
* currently specified record type.
*
* \param baseId The baseId of the record.
* \return void
*/
static void SetRecordBaseId(const char* baseId) noexcept;
/**
* \brief Set the inventory base id (i.e. the id this record should inherit
* its inventory contents from) of the temporary record stored on the server for
* the currently specified record type.
*
* \param inventoryBaseId The inventoryBaseId of the record.
* \return void
*/
static void SetRecordInventoryBaseId(const char* inventoryBaseId) noexcept;
/**
* \brief Set the subtype of the temporary record stored on the server for
* the currently specified record type.
*
* \param type The spell type.
* \return void
*/
static void SetRecordSubtype(unsigned int subtype) noexcept;
/**
* \brief Set the name of the temporary record stored on the server for the
* currently specified record type.
*
* \param name The name of the record.
* \return void
*/
static void SetRecordName(const char* name) noexcept;
/**
* \brief Set the model of the temporary record stored on the server for the
* currently specified record type.
*
* \param model The model of the record.
* \return void
*/
static void SetRecordModel(const char* model) noexcept;
/**
* \brief Set the icon of the temporary record stored on the server for the
* currently specified record type.
*
* \param icon The icon of the record.
* \return void
*/
static void SetRecordIcon(const char* icon) noexcept;
/**
* \brief Set the script of the temporary record stored on the server for the
* currently specified record type.
*
* \param script The script of the record.
* \return void
*/
static void SetRecordScript(const char* script) noexcept;
/**
* \brief Set the enchantment id of the temporary record stored on the server
* for the currently specified record type.
*
* \param enchantmentId The enchantment id of the record.
* \return void
*/
static void SetRecordEnchantmentId(const char* enchantmentId) noexcept;
/**
* \brief Set the enchantment charge of the temporary record stored on the server
* for the currently specified record type.
*
* \param enchantmentCharge The enchantmentCharge of the record.
* \return void
*/
static void SetRecordEnchantmentCharge(int enchantmentCharge) noexcept;
/**
* \brief Set the auto-calculation flag value of the temporary record stored
* on the server for the currently specified record type.
*
* \param autoCalc The auto-calculation flag value of the record.
* \return void
*/
static void SetRecordAutoCalc(int autoCalc) noexcept;
/**
* \brief Set the charge of the temporary record stored on the server for the
* currently specified record type.
*
* \param charge The charge of the record.
* \return void
*/
static void SetRecordCharge(int charge) noexcept;
/**
* \brief Set the cost of the temporary record stored on the server for the
* currently specified record type.
*
* \param cost The cost of the record.
* \return void
*/
static void SetRecordCost(int cost) noexcept;
/**
* \brief Set the flags of the temporary record stored on the server for the
* currently specified record type.
*
* \param flags The flags of the record.
* \return void
*/
static void SetRecordFlags(int flags) noexcept;
/**
* \brief Set the value of the temporary record stored on the server for the
* currently specified record type.
*
* \param value The value of the record.
* \return void
*/
static void SetRecordValue(int value) noexcept;
/**
* \brief Set the weight of the temporary record stored on the server for the
* currently specified record type.
*
* \param weight The weight of the record.
* \return void
*/
static void SetRecordWeight(double weight) noexcept;
/**
* \brief Set the armor rating of the temporary record stored on the server
* for the currently specified record type.
*
* \param armorRating The armor rating of the record.
* \return void
*/
static void SetRecordArmorRating(int armorRating) noexcept;
/**
* \brief Set the health of the temporary record stored on the server for the
* currently specified record type.
*
* \param health The health of the record.
* \return void
*/
static void SetRecordHealth(int health) noexcept;
/**
* \brief Set the chop damage of the temporary record stored on the server for the
* currently specified record type.
*
* \param minDamage The minimum damage of the record.
* \param maxDamage The maximum damage of the record.
* \return void
*/
static void SetRecordDamageChop(unsigned int minDamage, unsigned int maxDamage) noexcept;
/**
* \brief Set the slash damage of the temporary record stored on the server for the
* currently specified record type.
*
* \param minDamage The minimum damage of the record.
* \param maxDamage The maximum damage of the record.
* \return void
*/
static void SetRecordDamageSlash(unsigned int minDamage, unsigned int maxDamage) noexcept;
/**
* \brief Set the thrust damage of the temporary record stored on the server for the
* currently specified record type.
*
* \param minDamage The minimum damage of the record.
* \param maxDamage The maximum damage of the record.
* \return void
*/
static void SetRecordDamageThrust(unsigned int minDamage, unsigned int maxDamage) noexcept;
/**
* \brief Set the reach of the temporary record stored on the server for the
* currently specified record type.
*
* \param reach The reach of the record.
* \return void
*/
static void SetRecordReach(double reach) noexcept;
/**
* \brief Set the speed of the temporary record stored on the server for the
* currently specified record type.
*
* \param speed The speed of the record.
* \return void
*/
static void SetRecordSpeed(double speed) noexcept;
/**
* \brief Set whether the temporary record stored on the server for the
* currently specified record type is a key.
*
* Note: This is only applicable to Miscellaneous records.
*
* \param keyState Whether the record is a key.
* \return void
*/
static void SetRecordKeyState(bool keyState) noexcept;
/**
* \brief Set whether the temporary record stored on the server for the
* currently specified record type is a scroll.
*
* Note: This is only applicable to Book records.
*
* \param scrollState Whether the record is a scroll.
* \return void
*/
static void SetRecordScrollState(bool scrollState) noexcept;
/**
* \brief Set the skill ID of the temporary record stored on the server for the
* currently specified record type.
*
* \param skillId The skill ID of the record.
* \return void
*/
static void SetRecordSkillId(int skillId) noexcept;
/**
* \brief Set the text of the temporary record stored on the server for the
* currently specified record type.
*
* \param text The text of the record.
* \return void
*/
static void SetRecordText(const char* text) noexcept;
/**
* \brief Set the hair of the temporary record stored on the server for the
* currently specified record type.
*
* \param hair The hair of the record.
* \return void
*/
static void SetRecordHair(const char* hair) noexcept;
/**
* \brief Set the head of the temporary record stored on the server for the
* currently specified record type.
*
* \param hair The head of the record.
* \return void
*/
static void SetRecordHead(const char* head) noexcept;
/**
* \brief Set the gender of the temporary record stored on the server for the
* currently specified record type (0 for female, 1 for male).
*
* \param hair The race of the record.
* \return void
*/
static void SetRecordGender(unsigned int gender) noexcept;
/**
* \brief Set the race of the temporary record stored on the server for the
* currently specified record type.
*
* \param hair The race of the record.
* \return void
*/
static void SetRecordRace(const char* race) noexcept;
/**
* \brief Set the character class of the temporary record stored on the server
* for the currently specified record type.
*
* \param hair The character class of the record.
* \return void
*/
static void SetRecordClass(const char* charClass) noexcept;
/**
* \brief Set the faction of the temporary record stored on the server for the
* currently specified record type.
*
* \param faction The faction of the record.
* \return void
*/
static void SetRecordFaction(const char* faction) noexcept;
/**
* \brief Set the level of the temporary record stored on the server for the
* currently specified record type.
*
* \param value The level of the record.
* \return void
*/
static void SetRecordLevel(int level) noexcept;
/**
* \brief Set the id of the record at a certain index in the records stored on the server.
*
* When resending a received RecordsDynamic packet, this allows you to set the server-generated
* id of a record without having to clear and recreate the packet.
*
* \param index The index of the record.
* \param id The id of the record.
* \return void
*/
static void SetRecordIdByIndex(unsigned int index, const char* id) noexcept;
/**
* \brief Set the enchantment id of the record at a certain index in the records stored on
* the server.
*
* When resending a received RecordsDynamic packet, this allows you to set the server-generated
* enchantment id of a record without having to clear and recreate the packet.
*
* \param index The index of the record.
* \param enchantmentId The enchantment id of the record.
* \return void
*/
static void SetRecordEnchantmentIdByIndex(unsigned int index, const char* enchantmentId) noexcept;
/**
* \brief Set the ID of the temporary effect stored on the server.
*
* \param effectId The ID of the effect.
* \return void
*/
static void SetRecordEffectId(unsigned int effectId) noexcept;
/**
* \brief Set the ID of the attribute modified by the temporary effect stored on
* the server.
*
* \param attributeId The ID of the attribute.
* \return void
*/
static void SetRecordEffectAttribute(int attributeId) noexcept;
/**
* \brief Set the ID of the skill modified by the temporary effect stored on the
* server.
*
* \param skillId The ID of the skill.
* \return void
*/
static void SetRecordEffectSkill(int skillId) noexcept;
/**
* \brief Set the range type of the temporary effect stored on the server (0 for
* self, 1 for touch, 2 for target).
*
* \param rangeType The range type of the effect.
* \return void
*/
static void SetRecordEffectRangeType(unsigned int rangeType) noexcept;
/**
* \brief Set the area of the temporary effect stored on the server.
*
* \param area The area of the effect.
* \return void
*/
static void SetRecordEffectArea(int area) noexcept;
/**
* \brief Set the duration of the temporary effect stored on the server.
*
* \param duration The duration of the effect.
* \return void
*/
static void SetRecordEffectDuration(int duration) noexcept;
/**
* \brief Set the maximum magnitude of the temporary effect stored on the server.
*
* \param magnitudeMax The maximum magnitude of the effect.
* \return void
*/
static void SetRecordEffectMagnitudeMax(int magnitudeMax) noexcept;
/**
* \brief Set the minimum magnitude of the temporary effect stored on the server.
*
* \param magnitudeMin The minimum magnitude of the effect.
* \return void
*/
static void SetRecordEffectMagnitudeMin(int magnitudeMin) noexcept;
/**
* \brief Set the type of the temporary body part stored on the server.
*
* \param partType The type of the body part.
* \return void
*/
static void SetRecordBodyPartType(unsigned int partType) noexcept;
/**
* \brief Set the id of the male version of the temporary body part stored on the
* server.
*
* \param partId The id of the body part.
* \return void
*/
static void SetRecordBodyPartIdForMale(const char* partId) noexcept;
/**
* \brief Set the id of the female version of the temporary body part stored on the
* server.
*
* \param partId The id of the body part.
* \return void
*/
static void SetRecordBodyPartIdForFemale(const char* partId) noexcept;
/**
* \brief Set the id of the of the temporary inventory item stored on the server.
*
* \param partId The id of the inventory item.
* \return void
*/
static void SetRecordInventoryItemId(const char* itemId) noexcept;
/**
* \brief Set the count of the of the temporary inventory item stored on the server.
*
* \param count The count of the inventory item.
* \return void
*/
static void SetRecordInventoryItemCount(unsigned int count) noexcept;
/**
* \brief Add a copy of the server's temporary record of the current specified
* type to the stored records.
*
* In the process, the server's temporary record will automatically be cleared
* so a new one can be set up.
*
* \return void
*/
static void AddRecord() noexcept;
/**
* \brief Add a copy of the server's temporary effect to the temporary record
* of the current specified type.
*
* In the process, the server's temporary effect will automatically be cleared
* so a new one can be set up.
*
* \return void
*/
static void AddRecordEffect() noexcept;
/**
* \brief Add a copy of the server's temporary body part to the temporary record
* of the current specified type.
*
* In the process, the server's temporary body part will automatically be cleared
* so a new one can be set up.
*
* \return void
*/
static void AddRecordBodyPart() noexcept;
/**
* \brief Add a copy of the server's temporary inventory item to the temporary record
* of the current specified type.
*
* In the process, the server's temporary inventory item will automatically be cleared
* so a new one can be set up.
*
* Note: Any items added this way will be ignored if the record already has a valid
* inventoryBaseId.
*
* \return void
*/
static void AddRecordInventoryItem() noexcept;
/**
* \brief Send a RecordDynamic packet with the current specified record type.
*
* \param pid The player ID attached to the packet.
* \param sendToOtherPlayers Whether this packet should be sent to players other than the
* player attached to the packet (false by default).
* \param skipAttachedPlayer Whether the packet should skip being sent to the player attached
* to the packet (false by default).
* \return void
*/
static void SendRecordDynamic(unsigned short pid, bool sendToOtherPlayers, bool skipAttachedPlayer) noexcept;
};
#endif //OPENMW_RECORDSDYNAMICAPI_HPP

@ -15,6 +15,7 @@
#include <Script/Functions/Objects.hpp>
#include <Script/Functions/Positions.hpp>
#include <Script/Functions/Quests.hpp>
#include <Script/Functions/RecordsDynamic.hpp>
#include <Script/Functions/Shapeshift.hpp>
#include <Script/Functions/Server.hpp>
#include <Script/Functions/Settings.hpp>
@ -138,6 +139,7 @@ public:
MISCELLANEOUSAPI,
POSITIONAPI,
QUESTAPI,
RECORDSDYNAMICAPI,
SHAPESHIFTAPI,
SERVERAPI,
SETTINGSAPI,

@ -8,6 +8,7 @@
#include <components/openmw-mp/Utils.hpp>
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
#include "../mwmp/Worldstate.hpp"
/*
End of tes3mp addition
*/
@ -306,6 +307,18 @@ namespace MWClass
newItem.mName=newName;
newItem.mData.mEnchant=enchCharge;
newItem.mEnchant=enchId;
/*
Start of tes3mp addition
Send the newly created record to the server and expect it to be
returned with a server-set id
*/
mwmp::Main::get().getNetworking()->getWorldstate()->sendArmorRecord(&newItem, ref->mBase->mId);
/*
End of tes3mp addition
*/
const ESM::Armor *record = MWBase::Environment::get().getWorld()->createRecord (newItem);
return record->mId;
}

@ -191,6 +191,18 @@ namespace MWClass
newItem.mData.mIsScroll = 1;
newItem.mData.mEnchant=enchCharge;
newItem.mEnchant=enchId;
/*
Start of tes3mp addition
Send the newly created record to the server and expect it to be
returned with a server-set id
*/
mwmp::Main::get().getNetworking()->getWorldstate()->sendBookRecord(&newItem, ref->mBase->mId);
/*
End of tes3mp addition
*/
const ESM::Book *record = MWBase::Environment::get().getWorld()->createRecord (newItem);
return record->mId;
}

@ -241,6 +241,18 @@ namespace MWClass
newItem.mName=newName;
newItem.mData.mEnchant=enchCharge;
newItem.mEnchant=enchId;
/*
Start of tes3mp addition
Send the newly created record to the server and expect it to be
returned with a server-set id
*/
mwmp::Main::get().getNetworking()->getWorldstate()->sendClothingRecord(&newItem, ref->mBase->mId);
/*
End of tes3mp addition
*/
const ESM::Clothing *record = MWBase::Environment::get().getWorld()->createRecord (newItem);
return record->mId;
}

@ -402,6 +402,18 @@ namespace MWClass
newItem.mData.mEnchant=enchCharge;
newItem.mEnchant=enchId;
newItem.mData.mFlags |= ESM::Weapon::Magical;
/*
Start of tes3mp addition
Send the newly created record to the server and expect it to be
returned with a server-set id
*/
mwmp::Main::get().getNetworking()->getWorldstate()->sendWeaponRecord(&newItem, ref->mBase->mId);
/*
End of tes3mp addition
*/
const ESM::Weapon *record = MWBase::Environment::get().getWorld()->createRecord (newItem);
return record->mId;
}

@ -91,17 +91,6 @@ namespace MWGui
case MWMechanics::Alchemy::Result_Success:
winMgr->messageBox("#{sPotionSuccess}");
winMgr->playSound("potion success");
/*
Start of tes3mp addition
Include a messagebox notifying players that player-made potions are not synced yet
*/
MWBase::Environment::get().getWindowManager()->messageBox("Player-made potions are not synchronized in multiplayer yet and they will not show up for the server or other players.");
/*
End of tes3mp addition
*/
break;
case MWMechanics::Alchemy::Result_NoEffects:
case MWMechanics::Alchemy::Result_RandomFailure:

@ -361,16 +361,6 @@ namespace MWGui
{
MWBase::Environment::get().getWindowManager()->playSound("enchant success");
MWBase::Environment::get().getWindowManager()->messageBox ("#{sEnchantmentMenu12}");
/*
Start of tes3mp addition
Include a messagebox notifying players that player-made enchantments are not synced yet
*/
MWBase::Environment::get().getWindowManager()->messageBox("Player-made enchantments are not synchronized in multiplayer yet and they will not show up for other players.");
/*
End of tes3mp addition
*/
}
else
{

@ -12,7 +12,8 @@
Include additional headers for multiplayer purposes
*/
#include "../mwmp/Main.hpp"
#include "../mwmp/LocalPlayer.hpp"
#include "../mwmp/Networking.hpp"
#include "../mwmp/Worldstate.hpp"
/*
End of tes3mp addition
*/
@ -419,21 +420,22 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->playSound ("Mysticism Hit");
/*
Start of tes3mp change (major)
Don't create a record and don't add the spell to the player's spellbook;
instead just send its record to the server and expect the server to add it
to the player's spellbook
*/
/*
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->createRecord(mSpell);
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
MWMechanics::Spells& spells = stats.getSpells();
spells.add (spell->mId);
/*
Start of tes3mp addition
Send an ID_PLAYER_SPELLBOOK packet every time a player buys a custom spell from
the Spellmaking screen
Include a messagebox notifying players that custom spells are not synced yet
spells.add(spell->mId);
*/
MWBase::Environment::get().getWindowManager()->messageBox("Custom spells are not synchronized in multiplayer yet and their effects cannot be seen by other players in most cases.");
mwmp::Main::get().getNetworking()->getWorldstate()->sendSpellRecord(&mSpell);
/*
End of tes3mp addition
*/

@ -15,6 +15,19 @@
#include <components/esm/loadgmst.hpp>
#include <components/esm/loadmgef.hpp>
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include <components/openmw-mp/Log.hpp>
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
#include "../mwmp/Worldstate.hpp"
/*
End of tes3mp addition
*/
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -302,11 +315,27 @@ void MWMechanics::Alchemy::addPotion (const std::string& name)
newRecord.mEffects.mList = mEffects;
/*
Start of tes3mp change (major)
Don't create a record and don't add the potion to the player's inventory;
instead just send its record to the server and expect the server to add it
to the player's inventory
*/
/*
const ESM::Potion* record = getRecord(newRecord);
if (!record)
record = MWBase::Environment::get().getWorld()->createRecord (newRecord);
{
record = MWBase::Environment::get().getWorld()->createRecord(newRecord);
}
mAlchemist.getClass().getContainerStore (mAlchemist).add (record->mId, 1, mAlchemist);
*/
mwmp::Main::get().getNetworking()->getWorldstate()->sendPotionRecord(&newRecord);
/*
End of tes3mp change (major)
*/
}
void MWMechanics::Alchemy::increaseSkill()

@ -2,6 +2,20 @@
#include <components/misc/rng.hpp>
/*
Start of tes3mp addition
Include additional headers for multiplayer purposes
*/
#include <components/openmw-mp/Log.hpp>
#include "../mwmp/Main.hpp"
#include "../mwmp/Networking.hpp"
#include "../mwmp/LocalPlayer.hpp"
#include "../mwmp/Worldstate.hpp"
/*
End of tes3mp addition
*/
#include "../mwworld/manualref.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
@ -86,15 +100,36 @@ namespace MWMechanics
// Apply the enchantment
const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment);
std::string newItemId = mOldItemPtr.getClass().applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName);
// Add the new item to player inventory and remove the old one
/*
Start of tes3mp change (major)
Send the enchantment's record to the server
Don't add the new item to the player's inventory and instead expect the server to
add it
Before using the applyEnchantment() method, remove the old item and any money paid for
the enchantment and send the player's inventory to the server
The applyEnchantment() method is where the record of the newly enchanted will be sent
to the server, causing the server to send back the player's inventory with the new item
included
*/
mwmp::Main::get().getNetworking()->getWorldstate()->sendEnchantmentRecord(enchantmentPtr);
store.remove(mOldItemPtr, 1, player);
store.add(newItemId, 1, player);
if(!mSelfEnchanting)
if (!mSelfEnchanting)
payForEnchantment();
mwmp::Main::get().getLocalPlayer()->sendInventory();
std::string newItemId = mOldItemPtr.getClass().applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName);
/*
End of tes3mp change (major)
*/
return true;
}

@ -152,7 +152,7 @@ void DedicatedPlayer::move(float dt)
void DedicatedPlayer::setBaseInfo()
{
// Use the previous race if the new one doesn't exist
if (!RecordHelper::doesRaceExist(npc.mRace))
if (!RecordHelper::doesRaceRecordExist(npc.mRace))
npc.mRace = previousRace;
if (!reference)
@ -184,7 +184,7 @@ void DedicatedPlayer::setShapeshift()
if (creatureRefId != previousCreatureRefId || displayCreatureName != previousDisplayCreatureName)
{
if (!creatureRefId.empty() && RecordHelper::doesCreatureExist(creatureRefId))
if (!creatureRefId.empty() && RecordHelper::doesCreatureRecordExist(creatureRefId))
{
deleteReference();

@ -394,7 +394,7 @@ void ObjectList::spawnObjects(MWWorld::CellStore* cellStore)
// Ignore generic dynamic refIds because they could be anything on other clients
if (baseObject.refId.find("$dynamic") != string::npos)
continue;
else if (!RecordHelper::doesCreatureExist(baseObject.refId) && !RecordHelper::doesNpcExist(baseObject.refId))
else if (!RecordHelper::doesCreatureRecordExist(baseObject.refId) && !RecordHelper::doesNpcRecordExist(baseObject.refId))
{
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Ignored spawning of invalid object %s", baseObject.refId.c_str());
continue;

@ -6,72 +6,803 @@
#include "RecordHelper.hpp"
bool RecordHelper::doesCreatureExist(const std::string& refId)
bool RecordHelper::doesClassRecordExist(const std::string& id)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
return world->getStore().get<ESM::Creature>().search(refId);
return world->getStore().get<ESM::Class>().search(id);
}
bool RecordHelper::doesNpcExist(const std::string& refId)
bool RecordHelper::doesRaceRecordExist(const std::string& id)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
return world->getStore().get<ESM::NPC>().search(refId);
return world->getStore().get<ESM::Race>().search(id);
}
bool RecordHelper::doesRaceExist(const std::string& raceId)
bool RecordHelper::doesCreatureRecordExist(const std::string& id)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
return world->getStore().get<ESM::Race>().search(raceId);
return world->getStore().get<ESM::Creature>().search(id);
}
std::string RecordHelper::createCreatureRecord(const ESM::Creature& creature)
bool RecordHelper::doesNpcRecordExist(const std::string& id)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
return world->createRecord(creature)->mId;
return world->getStore().get<ESM::NPC>().search(id);
}
std::string RecordHelper::createNpcRecord(const ESM::NPC& npc)
bool RecordHelper::doesEnchantmentRecordExist(const std::string& id)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
return world->createRecord(npc)->mId;
return world->getStore().get<ESM::Enchantment>().search(id);
}
void RecordHelper::overrideCreatureRecord(const ESM::Creature& creature)
bool RecordHelper::doesPotionRecordExist(const std::string& id)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(creature);
return world->getStore().get<ESM::Potion>().search(id);
}
void RecordHelper::overrideNpcRecord(const ESM::NPC& npc)
bool RecordHelper::doesSpellRecordExist(const std::string& id)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(npc);
return world->getStore().get<ESM::Spell>().search(id);
}
void RecordHelper::overrideEnchantmentRecord(const ESM::Enchantment& enchantment)
bool RecordHelper::doesArmorRecordExist(const std::string& id)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(enchantment);
return world->getStore().get<ESM::Armor>().search(id);
}
void RecordHelper::overridePotionRecord(const ESM::Potion& potion)
bool RecordHelper::doesBookRecordExist(const std::string& id)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(potion);
return world->getStore().get<ESM::Book>().search(id);
}
void RecordHelper::overrideSpellRecord(const ESM::Spell& spell)
bool RecordHelper::doesClothingRecordExist(const std::string& id)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(spell);
return world->getStore().get<ESM::Clothing>().search(id);
}
bool RecordHelper::doesMiscellaneousRecordExist(const std::string& id)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
return world->getStore().get<ESM::Miscellaneous>().search(id);
}
bool RecordHelper::doesWeaponRecordExist(const std::string& id)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
return world->getStore().get<ESM::Weapon>().search(id);
}
std::string RecordHelper::createCreatureRecord(const ESM::Creature& record)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
return world->createRecord(record)->mId;
}
std::string RecordHelper::createNpcRecord(const ESM::NPC& record)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
return world->createRecord(record)->mId;
}
void RecordHelper::overrideCreatureRecord(const mwmp::CreatureRecord& record)
{
const ESM::Creature &recordData = record.data;
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
return;
}
bool isExistingId = doesCreatureRecordExist(recordData.mId);
MWBase::World *world = MWBase::Environment::get().getWorld();
if (record.baseId.empty())
{
world->getModifiableStore().overrideRecord(recordData);
}
else if (doesCreatureRecordExist(record.baseId))
{
const ESM::Creature *baseData = world->getStore().get<ESM::Creature>().search(record.baseId);
ESM::Creature finalData = *baseData;
finalData.mId = recordData.mId;
if (record.baseOverrides.hasName)
finalData.mName = recordData.mName;
if (record.baseOverrides.hasModel)
finalData.mModel = recordData.mModel;
if (record.baseOverrides.hasSubtype)
finalData.mData.mType = recordData.mData.mType;
if (record.baseOverrides.hasLevel)
finalData.mData.mLevel = recordData.mData.mLevel;
if (record.baseOverrides.hasFlags)
finalData.mFlags = recordData.mFlags;
if (record.baseOverrides.hasScript)
finalData.mScript = recordData.mScript;
if (!record.inventoryBaseId.empty() && doesNpcRecordExist(record.inventoryBaseId))
finalData.mInventory.mList = world->getStore().get<ESM::Creature>().search(record.inventoryBaseId)->mInventory.mList;
else if (record.baseOverrides.hasInventory)
finalData.mInventory.mList = recordData.mInventory.mList;
world->getModifiableStore().overrideRecord(finalData);
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
}
void RecordHelper::overrideNpcRecord(const mwmp::NpcRecord& record)
{
const ESM::NPC &recordData = record.data;
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
return;
}
bool isExistingId = doesNpcRecordExist(recordData.mId);
MWBase::World *world = MWBase::Environment::get().getWorld();
if (record.baseId.empty())
{
if (!doesRaceRecordExist(recordData.mRace))
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new NPC record with invalid race provided");
return;
}
else if (!doesClassRecordExist(recordData.mClass))
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new NPC record with invalid class provided");
return;
}
else
world->getModifiableStore().overrideRecord(recordData);
}
else if (doesNpcRecordExist(record.baseId))
{
const ESM::NPC *baseData = world->getStore().get<ESM::NPC>().search(record.baseId);
ESM::NPC finalData = *baseData;
finalData.mId = recordData.mId;
if (record.baseOverrides.hasName)
finalData.mName = recordData.mName;
if (record.baseOverrides.hasFlags)
finalData.mFlags = recordData.mFlags;
// Because the gender is part of mFlags and can easily be set incorrectly there,
// we handle it separately here
if (record.baseOverrides.hasGender)
finalData.setIsMale(recordData.isMale());
else
finalData.setIsMale(baseData->isMale());
if (!record.data.mRace.empty())
finalData.mRace = recordData.mRace;
if (record.baseOverrides.hasModel)
finalData.mModel = recordData.mModel;
if (record.baseOverrides.hasHair)
finalData.mHair = recordData.mHair;
if (record.baseOverrides.hasHead)
finalData.mHead = recordData.mHead;
if (!recordData.mClass.empty())
finalData.mClass = recordData.mClass;
if (record.baseOverrides.hasFaction)
finalData.mFaction = recordData.mFaction;
if (record.baseOverrides.hasScript)
finalData.mScript = recordData.mScript;
if (record.baseOverrides.hasLevel)
finalData.mNpdt.mLevel = recordData.mNpdt.mLevel;
if (record.baseOverrides.hasAutoCalc)
finalData.mNpdtType = recordData.mNpdtType;
if (!record.inventoryBaseId.empty() && doesNpcRecordExist(record.inventoryBaseId))
finalData.mInventory.mList = world->getStore().get<ESM::NPC>().search(record.inventoryBaseId)->mInventory.mList;
else if (record.baseOverrides.hasInventory)
finalData.mInventory.mList = recordData.mInventory.mList;
world->getModifiableStore().overrideRecord(finalData);
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
}
void RecordHelper::overrideEnchantmentRecord(const mwmp::EnchantmentRecord& record)
{
const ESM::Enchantment &recordData = record.data;
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
return;
}
bool isExistingId = doesEnchantmentRecordExist(recordData.mId);
MWBase::World *world = MWBase::Environment::get().getWorld();
if (record.baseId.empty())
{
world->getModifiableStore().overrideRecord(recordData);
}
else if (doesEnchantmentRecordExist(record.baseId))
{
const ESM::Enchantment *baseData = world->getStore().get<ESM::Enchantment>().search(record.baseId);
ESM::Enchantment finalData = *baseData;
finalData.mId = recordData.mId;
if (record.baseOverrides.hasSubtype)
finalData.mData.mType = recordData.mData.mType;
if (record.baseOverrides.hasCost)
finalData.mData.mCost = recordData.mData.mCost;
if (record.baseOverrides.hasCharge)
finalData.mData.mCharge = recordData.mData.mCharge;
if (record.baseOverrides.hasAutoCalc)
finalData.mData.mAutocalc = recordData.mData.mAutocalc;
if (record.baseOverrides.hasEffects)
finalData.mEffects.mList = recordData.mEffects.mList;
world->getModifiableStore().overrideRecord(finalData);
}
}
void RecordHelper::overridePotionRecord(const mwmp::PotionRecord& record)
{
const ESM::Potion &recordData = record.data;
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
return;
}
bool isExistingId = doesPotionRecordExist(recordData.mId);
MWBase::World *world = MWBase::Environment::get().getWorld();
if (record.baseId.empty())
{
world->getModifiableStore().overrideRecord(recordData);
}
else if (doesPotionRecordExist(record.baseId))
{
const ESM::Potion *baseData = world->getStore().get<ESM::Potion>().search(record.baseId);
ESM::Potion finalData = *baseData;
finalData.mId = recordData.mId;
if (record.baseOverrides.hasName)
finalData.mName = recordData.mName;
if (record.baseOverrides.hasModel)
finalData.mModel = recordData.mModel;
if (record.baseOverrides.hasIcon)
finalData.mIcon = recordData.mIcon;
if (record.baseOverrides.hasWeight)
finalData.mData.mWeight = recordData.mData.mWeight;
if (record.baseOverrides.hasValue)
finalData.mData.mValue = recordData.mData.mValue;
if (record.baseOverrides.hasAutoCalc)
finalData.mData.mAutoCalc = recordData.mData.mAutoCalc;
if (record.baseOverrides.hasScript)
finalData.mScript = recordData.mScript;
if (record.baseOverrides.hasEffects)
finalData.mEffects.mList = recordData.mEffects.mList;
world->getModifiableStore().overrideRecord(finalData);
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
}
void RecordHelper::overrideSpellRecord(const mwmp::SpellRecord& record)
{
const ESM::Spell &recordData = record.data;
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
return;
}
bool isExistingId = doesSpellRecordExist(recordData.mId);
MWBase::World *world = MWBase::Environment::get().getWorld();
if (record.baseId.empty())
{
world->getModifiableStore().overrideRecord(recordData);
}
else if (doesSpellRecordExist(record.baseId))
{
const ESM::Spell *baseData = world->getStore().get<ESM::Spell>().search(record.baseId);
ESM::Spell finalData = *baseData;
finalData.mId = recordData.mId;
if (record.baseOverrides.hasName)
finalData.mName = recordData.mName;
if (record.baseOverrides.hasSubtype)
finalData.mData.mType = recordData.mData.mType;
if (record.baseOverrides.hasCost)
finalData.mData.mCost = recordData.mData.mCost;
if (record.baseOverrides.hasFlags)
finalData.mData.mFlags = recordData.mData.mFlags;
if (record.baseOverrides.hasEffects)
finalData.mEffects.mList = recordData.mEffects.mList;
world->getModifiableStore().overrideRecord(finalData);
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
}
void RecordHelper::overrideArmorRecord(const mwmp::ArmorRecord& record)
{
const ESM::Armor &recordData = record.data;
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
return;
}
bool isExistingId = doesArmorRecordExist(recordData.mId);
MWBase::World *world = MWBase::Environment::get().getWorld();
if (record.baseId.empty())
{
if (!recordData.mEnchant.empty() && !doesEnchantmentRecordExist(recordData.mEnchant))
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new armor record with invalid enchantment provided");
return;
}
else
world->getModifiableStore().overrideRecord(recordData);
}
else if (doesArmorRecordExist(record.baseId))
{
const ESM::Armor *baseData = world->getStore().get<ESM::Armor>().search(record.baseId);
ESM::Armor finalData = *baseData;
finalData.mId = recordData.mId;
finalData.mParts.mParts.at(0);
if (record.baseOverrides.hasName)
finalData.mName = recordData.mName;
if (record.baseOverrides.hasModel)
finalData.mModel = recordData.mModel;
if (record.baseOverrides.hasIcon)
finalData.mIcon = recordData.mIcon;
if (record.baseOverrides.hasSubtype)
finalData.mData.mType = recordData.mData.mType;
if (record.baseOverrides.hasWeight)
finalData.mData.mWeight = recordData.mData.mWeight;
if (record.baseOverrides.hasValue)
finalData.mData.mValue = recordData.mData.mValue;
if (record.baseOverrides.hasHealth)
finalData.mData.mHealth = recordData.mData.mHealth;
if (record.baseOverrides.hasArmorRating)
finalData.mData.mArmor = recordData.mData.mArmor;
if (record.baseOverrides.hasEnchantmentId && doesEnchantmentRecordExist(recordData.mEnchant))
finalData.mEnchant = recordData.mEnchant;
if (record.baseOverrides.hasEnchantmentCharge)
finalData.mData.mEnchant = recordData.mData.mEnchant;
if (record.baseOverrides.hasScript)
finalData.mScript = recordData.mScript;
if (record.baseOverrides.hasBodyParts)
finalData.mParts.mParts = recordData.mParts.mParts;
world->getModifiableStore().overrideRecord(finalData);
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
}
void RecordHelper::overrideBookRecord(const mwmp::BookRecord& record)
{
const ESM::Book &recordData = record.data;
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
return;
}
bool isExistingId = doesBookRecordExist(recordData.mId);
MWBase::World *world = MWBase::Environment::get().getWorld();
if (record.baseId.empty())
{
if (!recordData.mEnchant.empty() && !doesEnchantmentRecordExist(recordData.mEnchant))
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new book record with invalid enchantment provided");
return;
}
else
world->getModifiableStore().overrideRecord(recordData);
}
else if (doesBookRecordExist(record.baseId))
{
const ESM::Book *baseData = world->getStore().get<ESM::Book>().search(record.baseId);
ESM::Book finalData = *baseData;
finalData.mId = recordData.mId;
if (record.baseOverrides.hasName)
finalData.mName = recordData.mName;
if (record.baseOverrides.hasModel)
finalData.mModel = recordData.mModel;
if (record.baseOverrides.hasIcon)
finalData.mIcon = recordData.mIcon;
if (record.baseOverrides.hasText)
finalData.mText = recordData.mText;
if (record.baseOverrides.hasWeight)
finalData.mData.mWeight = recordData.mData.mWeight;
if (record.baseOverrides.hasValue)
finalData.mData.mValue = recordData.mData.mValue;
if (record.baseOverrides.hasScrollState)
finalData.mData.mIsScroll = recordData.mData.mIsScroll;
if (record.baseOverrides.hasSkillId)
finalData.mData.mSkillId = recordData.mData.mSkillId;
if (record.baseOverrides.hasEnchantmentId && doesEnchantmentRecordExist(recordData.mEnchant))
finalData.mEnchant = recordData.mEnchant;
if (record.baseOverrides.hasEnchantmentCharge)
finalData.mData.mEnchant = recordData.mData.mEnchant;
if (record.baseOverrides.hasScript)
finalData.mScript = recordData.mScript;
world->getModifiableStore().overrideRecord(finalData);
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
}
void RecordHelper::overrideClothingRecord(const mwmp::ClothingRecord& record)
{
const ESM::Clothing &recordData = record.data;
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
return;
}
bool isExistingId = doesClothingRecordExist(recordData.mId);
MWBase::World *world = MWBase::Environment::get().getWorld();
if (record.baseId.empty())
{
if (!recordData.mEnchant.empty() && !doesEnchantmentRecordExist(recordData.mEnchant))
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new clothing record with invalid enchantment provided");
return;
}
else
world->getModifiableStore().overrideRecord(recordData);
}
else if (doesClothingRecordExist(record.baseId))
{
const ESM::Clothing *baseData = world->getStore().get<ESM::Clothing>().search(record.baseId);
ESM::Clothing finalData = *baseData;
finalData.mId = recordData.mId;
if (record.baseOverrides.hasName)
finalData.mName = recordData.mName;
if (record.baseOverrides.hasModel)
finalData.mModel = recordData.mModel;
if (record.baseOverrides.hasIcon)
finalData.mIcon = recordData.mIcon;
if (record.baseOverrides.hasSubtype)
finalData.mData.mType = recordData.mData.mType;
if (record.baseOverrides.hasWeight)
finalData.mData.mWeight = recordData.mData.mWeight;
if (record.baseOverrides.hasValue)
finalData.mData.mValue = recordData.mData.mValue;
if (record.baseOverrides.hasEnchantmentId && doesEnchantmentRecordExist(recordData.mEnchant))
finalData.mEnchant = recordData.mEnchant;
if (record.baseOverrides.hasEnchantmentCharge)
finalData.mData.mEnchant = recordData.mData.mEnchant;
if (record.baseOverrides.hasScript)
finalData.mScript = recordData.mScript;
if (record.baseOverrides.hasBodyParts)
finalData.mParts.mParts = recordData.mParts.mParts;
world->getModifiableStore().overrideRecord(finalData);
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
}
void RecordHelper::overrideMiscellaneousRecord(const mwmp::MiscellaneousRecord& record)
{
const ESM::Miscellaneous &recordData = record.data;
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
return;
}
bool isExistingId = doesMiscellaneousRecordExist(recordData.mId);
MWBase::World *world = MWBase::Environment::get().getWorld();
if (record.baseId.empty())
{
world->getModifiableStore().overrideRecord(recordData);
}
else if (doesMiscellaneousRecordExist(record.baseId))
{
const ESM::Miscellaneous *baseData = world->getStore().get<ESM::Miscellaneous>().search(record.baseId);
ESM::Miscellaneous finalData = *baseData;
finalData.mId = recordData.mId;
if (record.baseOverrides.hasName)
finalData.mName = recordData.mName;
if (record.baseOverrides.hasModel)
finalData.mModel = recordData.mModel;
if (record.baseOverrides.hasIcon)
finalData.mIcon = recordData.mIcon;
if (record.baseOverrides.hasWeight)
finalData.mData.mWeight = recordData.mData.mWeight;
if (record.baseOverrides.hasValue)
finalData.mData.mValue = recordData.mData.mValue;
if (record.baseOverrides.hasKeyState)
finalData.mData.mIsKey = recordData.mData.mIsKey;
if (record.baseOverrides.hasScript)
finalData.mScript = recordData.mScript;
world->getModifiableStore().overrideRecord(finalData);
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
}
void RecordHelper::overrideWeaponRecord(const mwmp::WeaponRecord& record)
{
const ESM::Weapon &recordData = record.data;
if (recordData.mId.empty())
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring record override with no id provided");
return;
}
bool isExistingId = doesWeaponRecordExist(recordData.mId);
MWBase::World *world = MWBase::Environment::get().getWorld();
if (record.baseId.empty())
{
if (!recordData.mEnchant.empty() && !doesEnchantmentRecordExist(recordData.mEnchant))
{
LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "Ignoring new weapon record with invalid enchantment provided");
return;
}
else
world->getModifiableStore().overrideRecord(recordData);
}
else if (doesWeaponRecordExist(record.baseId))
{
const ESM::Weapon *baseData = world->getStore().get<ESM::Weapon>().search(record.baseId);
ESM::Weapon finalData = *baseData;
finalData.mId = recordData.mId;
if (record.baseOverrides.hasName)
finalData.mName = recordData.mName;
if (record.baseOverrides.hasModel)
finalData.mModel = recordData.mModel;
if (record.baseOverrides.hasIcon)
finalData.mIcon = recordData.mIcon;
if (record.baseOverrides.hasSubtype)
finalData.mData.mType = recordData.mData.mType;
if (record.baseOverrides.hasWeight)
finalData.mData.mWeight = recordData.mData.mWeight;
if (record.baseOverrides.hasValue)
finalData.mData.mValue = recordData.mData.mValue;
if (record.baseOverrides.hasHealth)
finalData.mData.mHealth = recordData.mData.mHealth;
if (record.baseOverrides.hasSpeed)
finalData.mData.mSpeed = recordData.mData.mSpeed;
if (record.baseOverrides.hasReach)
finalData.mData.mReach = recordData.mData.mReach;
if (record.baseOverrides.hasDamageChop)
{
finalData.mData.mChop[0] = recordData.mData.mChop[0];
finalData.mData.mChop[1] = recordData.mData.mChop[1];
}
if (record.baseOverrides.hasDamageSlash)
{
finalData.mData.mSlash[0] = recordData.mData.mSlash[0];
finalData.mData.mSlash[1] = recordData.mData.mSlash[1];
}
if (record.baseOverrides.hasDamageThrust)
{
finalData.mData.mThrust[0] = recordData.mData.mThrust[0];
finalData.mData.mThrust[1] = recordData.mData.mThrust[1];
}
if (record.baseOverrides.hasFlags)
finalData.mData.mFlags = recordData.mData.mFlags;
if (record.baseOverrides.hasEnchantmentId && doesEnchantmentRecordExist(recordData.mEnchant))
finalData.mEnchant = recordData.mEnchant;
if (record.baseOverrides.hasEnchantmentCharge)
finalData.mData.mEnchant = recordData.mData.mEnchant;
if (record.baseOverrides.hasScript)
finalData.mScript = recordData.mScript;
world->getModifiableStore().overrideRecord(finalData);
}
if (isExistingId)
world->updatePtrsWithRefId(recordData.mId);
}
void RecordHelper::overrideCreatureRecord(const ESM::Creature& record)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(record);
}
void RecordHelper::overrideNpcRecord(const ESM::NPC& record)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(record);
}
void RecordHelper::overrideEnchantmentRecord(const ESM::Enchantment& record)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(record);
}
void RecordHelper::overridePotionRecord(const ESM::Potion& record)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(record);
}
void RecordHelper::overrideSpellRecord(const ESM::Spell& record)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(record);
}
void RecordHelper::overrideArmorRecord(const ESM::Armor& record)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(record);
}
void RecordHelper::overrideBookRecord(const ESM::Book& record)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(record);
}
void RecordHelper::overrideClothingRecord(const ESM::Clothing& record)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(record);
}
void RecordHelper::overrideMiscellaneousRecord(const ESM::Miscellaneous& record)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(record);
}
void RecordHelper::overrideWeaponRecord(const ESM::Weapon& record)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->getModifiableStore().overrideRecord(record);
}

@ -1,24 +1,57 @@
#ifndef OPENMW_RECORDHELPER_HPP
#define OPENMW_RECORDHELPER_HPP
#include <components/openmw-mp/Base/BaseWorldstate.hpp>
#include <components/esm/loadcrea.hpp>
#include <components/esm/loadnpc.hpp>
namespace RecordHelper
{
bool doesCreatureExist(const std::string& refId);
bool doesNpcExist(const std::string& refId);
bool doesRaceExist(const std::string& raceId);
bool doesClassRecordExist(const std::string& id);
bool doesRaceRecordExist(const std::string& id);
bool doesCreatureRecordExist(const std::string& id);
bool doesNpcRecordExist(const std::string& id);
bool doesEnchantmentRecordExist(const std::string& id);
bool doesPotionRecordExist(const std::string& id);
bool doesSpellRecordExist(const std::string& id);
bool doesArmorRecordExist(const std::string& id);
bool doesBookRecordExist(const std::string& id);
bool doesClothingRecordExist(const std::string& id);
bool doesMiscellaneousRecordExist(const std::string& id);
bool doesWeaponRecordExist(const std::string& id);
std::string createCreatureRecord(const ESM::Creature& record);
std::string createNpcRecord(const ESM::NPC& record);
void overrideCreatureRecord(const mwmp::CreatureRecord& record);
void overrideNpcRecord(const mwmp::NpcRecord& record);
void overrideEnchantmentRecord(const mwmp::EnchantmentRecord& record);
void overridePotionRecord(const mwmp::PotionRecord& record);
void overrideSpellRecord(const mwmp::SpellRecord& record);
void overrideArmorRecord(const mwmp::ArmorRecord& record);
void overrideBookRecord(const mwmp::BookRecord& record);
void overrideClothingRecord(const mwmp::ClothingRecord& record);
void overrideMiscellaneousRecord(const mwmp::MiscellaneousRecord& record);
void overrideWeaponRecord(const mwmp::WeaponRecord& record);
std::string createCreatureRecord(const ESM::Creature& creature);
std::string createNpcRecord(const ESM::NPC& npc);
void overrideCreatureRecord(const ESM::Creature& record);
void overrideNpcRecord(const ESM::NPC& record);
void overrideCreatureRecord(const ESM::Creature& creature);
void overrideNpcRecord(const ESM::NPC& npc);
void overrideEnchantmentRecord(const ESM::Enchantment& record);
void overridePotionRecord(const ESM::Potion& record);
void overrideSpellRecord(const ESM::Spell& record);
void overrideEnchantmentRecord(const ESM::Enchantment& enchantment);
void overridePotionRecord(const ESM::Potion& potion);
void overrideSpellRecord(const ESM::Spell& spell);
void overrideArmorRecord(const ESM::Armor& record);
void overrideBookRecord(const ESM::Book& record);
void overrideClothingRecord(const ESM::Clothing& record);
void overrideMiscellaneousRecord(const ESM::Miscellaneous& record);
void overrideWeaponRecord(const ESM::Weapon& record);
}

@ -10,6 +10,7 @@
#include "Worldstate.hpp"
#include "Main.hpp"
#include "Networking.hpp"
#include "RecordHelper.hpp"
using namespace mwmp;
using namespace std;
@ -32,6 +33,133 @@ Networking *Worldstate::getNetworking()
return mwmp::Main::get().getNetworking();
}
void Worldstate::addRecords()
{
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Received ID_RECORD_DYNAMIC with %i records of type %i",
recordsCount, recordsType);
if (recordsType == mwmp::RECORD_TYPE::SPELL)
{
for (auto &&record : spellRecords)
{
bool hasBaseId = !record.baseId.empty();
LOG_APPEND(Log::LOG_ERROR, "- spell record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
hasBaseId ? record.baseId.c_str() : "empty");
RecordHelper::overrideSpellRecord(record.data);
}
}
else if (recordsType == mwmp::RECORD_TYPE::POTION)
{
for (auto &&record : potionRecords)
{
bool hasBaseId = !record.baseId.empty();
LOG_APPEND(Log::LOG_ERROR, "- potion record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
hasBaseId ? record.baseId.c_str() : "empty");
RecordHelper::overridePotionRecord(record.data);
}
}
else if (recordsType == mwmp::RECORD_TYPE::ENCHANTMENT)
{
for (auto &&record : enchantmentRecords)
{
bool hasBaseId = !record.baseId.empty();
LOG_APPEND(Log::LOG_ERROR, "- enchantment record %s, %i\n-- baseId is %s", record.data.mId.c_str(), record.data.mData.mType,
hasBaseId ? record.baseId.c_str() : "empty");
RecordHelper::overrideEnchantmentRecord(record.data);
}
}
else if (recordsType == mwmp::RECORD_TYPE::CREATURE)
{
for (auto &&record : creatureRecords)
{
bool hasBaseId = !record.baseId.empty();
LOG_APPEND(Log::LOG_ERROR, "- creature record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
hasBaseId ? record.baseId.c_str() : "empty");
RecordHelper::overrideCreatureRecord(record);
}
}
else if (recordsType == mwmp::RECORD_TYPE::NPC)
{
for (auto &&record : npcRecords)
{
bool hasBaseId = !record.baseId.empty();
LOG_APPEND(Log::LOG_ERROR, "- NPC record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
hasBaseId ? record.baseId.c_str() : "empty");
RecordHelper::overrideNpcRecord(record);
}
}
else if (recordsType == mwmp::RECORD_TYPE::ARMOR)
{
for (auto &&record : armorRecords)
{
bool hasBaseId = !record.baseId.empty();
LOG_APPEND(Log::LOG_ERROR, "- armor record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
hasBaseId ? record.baseId.c_str() : "empty");
RecordHelper::overrideArmorRecord(record);
}
}
else if (recordsType == mwmp::RECORD_TYPE::BOOK)
{
for (auto &&record : bookRecords)
{
bool hasBaseId = !record.baseId.empty();
LOG_APPEND(Log::LOG_ERROR, "- book record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
hasBaseId ? record.baseId.c_str() : "empty");
RecordHelper::overrideBookRecord(record);
}
}
else if (recordsType == mwmp::RECORD_TYPE::CLOTHING)
{
for (auto &&record : clothingRecords)
{
bool hasBaseId = !record.baseId.empty();
LOG_APPEND(Log::LOG_ERROR, "- clothing record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
hasBaseId ? record.baseId.c_str() : "empty");
RecordHelper::overrideClothingRecord(record);
}
}
else if (recordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)
{
for (auto &&record : miscellaneousRecords)
{
bool hasBaseId = !record.baseId.empty();
LOG_APPEND(Log::LOG_ERROR, "- miscellaneous record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
hasBaseId ? record.baseId.c_str() : "empty");
RecordHelper::overrideMiscellaneousRecord(record);
}
}
else if (recordsType == mwmp::RECORD_TYPE::WEAPON)
{
for (auto &&record : weaponRecords)
{
bool hasBaseId = !record.baseId.empty();
LOG_APPEND(Log::LOG_ERROR, "- weapon record %s, %s\n-- baseId is %s", record.data.mId.c_str(), record.data.mName.c_str(),
hasBaseId ? record.baseId.c_str() : "empty");
RecordHelper::overrideWeaponRecord(record);
}
}
}
bool Worldstate::containsExploredMapTile(int cellX, int cellY)
{
for (const auto &mapTile : exploredMapTiles)
@ -118,3 +246,131 @@ void Worldstate::sendWeather(std::string region, int currentWeather, int nextWea
getNetworking()->getWorldstatePacket(ID_WORLD_WEATHER)->setWorldstate(this);
getNetworking()->getWorldstatePacket(ID_WORLD_WEATHER)->Send();
}
void Worldstate::sendEnchantmentRecord(const ESM::Enchantment* enchantment)
{
enchantmentRecords.clear();
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with enchantment");
recordsType = mwmp::RECORD_TYPE::ENCHANTMENT;
mwmp::EnchantmentRecord record;
record.data = *enchantment;
enchantmentRecords.push_back(record);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
}
void Worldstate::sendPotionRecord(const ESM::Potion* potion)
{
potionRecords.clear();
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with potion %s", potion->mName.c_str());
recordsType = mwmp::RECORD_TYPE::POTION;
mwmp::PotionRecord record;
record.data = *potion;
potionRecords.push_back(record);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
}
void Worldstate::sendSpellRecord(const ESM::Spell* spell)
{
spellRecords.clear();
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with spell %s", spell->mName.c_str());
recordsType = mwmp::RECORD_TYPE::SPELL;
mwmp::SpellRecord record;
record.data = *spell;
spellRecords.push_back(record);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
}
void Worldstate::sendArmorRecord(const ESM::Armor* armor, std::string baseId)
{
armorRecords.clear();
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with armor %s", armor->mName.c_str());
recordsType = mwmp::RECORD_TYPE::ARMOR;
mwmp::ArmorRecord record;
record.data = *armor;
record.baseId = baseId;
record.baseOverrides.hasName = true;
record.baseOverrides.hasEnchantmentId = true;
record.baseOverrides.hasEnchantmentCharge = true;
armorRecords.push_back(record);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
}
void Worldstate::sendBookRecord(const ESM::Book* book, std::string baseId)
{
bookRecords.clear();
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with book %s", book->mName.c_str());
recordsType = mwmp::RECORD_TYPE::BOOK;
mwmp::BookRecord record;
record.data = *book;
record.baseId = baseId;
record.baseOverrides.hasName = true;
record.baseOverrides.hasEnchantmentId = true;
record.baseOverrides.hasEnchantmentCharge = true;
bookRecords.push_back(record);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
}
void Worldstate::sendClothingRecord(const ESM::Clothing* clothing, std::string baseId)
{
clothingRecords.clear();
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with clothing %s", clothing->mName.c_str());
recordsType = mwmp::RECORD_TYPE::CLOTHING;
mwmp::ClothingRecord record;
record.data = *clothing;
record.baseId = baseId;
record.baseOverrides.hasName = true;
record.baseOverrides.hasEnchantmentId = true;
record.baseOverrides.hasEnchantmentCharge = true;
clothingRecords.push_back(record);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
}
void Worldstate::sendWeaponRecord(const ESM::Weapon* weapon, std::string baseId)
{
weaponRecords.clear();
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Sending ID_RECORD_DYNAMIC with weapon %s", weapon->mName.c_str());
recordsType = mwmp::RECORD_TYPE::WEAPON;
mwmp::WeaponRecord record;
record.data = *weapon;
record.baseId = baseId;
record.baseOverrides.hasName = true;
record.baseOverrides.hasEnchantmentId = true;
record.baseOverrides.hasEnchantmentCharge = true;
weaponRecords.push_back(record);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->setWorldstate(this);
getNetworking()->getWorldstatePacket(ID_RECORD_DYNAMIC)->Send();
}

@ -13,8 +13,9 @@ namespace mwmp
Worldstate();
virtual ~Worldstate();
bool containsExploredMapTile(int cellX, int cellY);
void addRecords();
bool containsExploredMapTile(int cellX, int cellY);
void markExploredMapTile(int cellX, int cellY);
void setMapExplored();
@ -23,6 +24,15 @@ namespace mwmp
void sendMapExplored(int cellX, int cellY, const std::vector<char>& imageData);
void sendWeather(std::string region, int currentWeather, int nextWeather, int queuedWeather, float transitionFactor);
void sendEnchantmentRecord(const ESM::Enchantment* enchantment);
void sendPotionRecord(const ESM::Potion* potion);
void sendSpellRecord(const ESM::Spell* spell);
void sendArmorRecord(const ESM::Armor* armor, std::string baseRefId = "");
void sendBookRecord(const ESM::Book* book, std::string baseRefId = "");
void sendClothingRecord(const ESM::Clothing* clothing, std::string baseRefId = "");
void sendWeaponRecord(const ESM::Weapon* weapon, std::string baseRefId = "");
private:
std::vector<MapTile> exploredMapTiles;

@ -15,7 +15,7 @@ namespace mwmp
virtual void Do(WorldstatePacket &packet, Worldstate &worldstate)
{
// Placeholder
worldstate.addRecords();
}
};
}

@ -3,12 +3,167 @@
#include <vector>
#include <components/esm/loadalch.hpp>
#include <components/esm/loadarmo.hpp>
#include <components/esm/loadbook.hpp>
#include <components/esm/loadclot.hpp>
#include <components/esm/loadcrea.hpp>
#include <components/esm/loadench.hpp>
#include <components/esm/loadmisc.hpp>
#include <components/esm/loadnpc.hpp>
#include <components/esm/loadspel.hpp>
#include <components/esm/loadweap.hpp>
#include <components/openmw-mp/Base/BaseStructs.hpp>
#include <RakNetTypes.h>
namespace mwmp
{
enum RECORD_TYPE
{
ARMOR,
BOOK,
CLOTHING,
CREATURE,
ENCHANTMENT,
MISCELLANEOUS,
NPC,
POTION,
SPELL,
WEAPON
};
// When using an existing record as a base, this struct tracks which changes
// need to be made to it
//
// Note: These can't be replaced with checks for empty strings or numerical
// values of 0 because you want to be able to blank out strings or
// set values of 0 through overrides, i.e. if someone is in the
// Mages Guild faction, you want to be able to remove them from it
// by using a blank faction string as an override
//
// There are, however, a few values that are not allowed to be blanked
// out in a record, such as races or classes for NPCs, and those
// should rely on checks for empty strings instead of having a
// boolean here
struct BaseOverrides
{
bool hasSubtype = false;
bool hasName = false;
bool hasModel = false;
bool hasIcon = false;
bool hasScript = false;
bool hasEnchantmentId = false;
bool hasEnchantmentCharge = false;
bool hasEffects = false;
bool hasBodyParts = false;
bool hasInventory = false;
bool hasAutoCalc = false;
bool hasCharge = false;
bool hasCost = false;
bool hasFlags = false;
bool hasValue = false;
bool hasWeight = false;
bool hasArmorRating = false;
bool hasHealth = false;
bool hasDamageChop = false;
bool hasDamageSlash = false;
bool hasDamageThrust = false;
bool hasReach = false;
bool hasSpeed = false;
bool hasKeyState = false;
bool hasScrollState = false;
bool hasSkillId = false;
bool hasText = false;
bool hasHair = false;
bool hasHead = false;
bool hasGender = false;
bool hasFaction = false;
bool hasLevel = false;
};
struct ArmorRecord
{
ESM::Armor data;
std::string baseId;
BaseOverrides baseOverrides;
};
struct BookRecord
{
ESM::Book data;
std::string baseId;
BaseOverrides baseOverrides;
};
struct ClothingRecord
{
ESM::Clothing data;
std::string baseId;
BaseOverrides baseOverrides;
};
struct CreatureRecord
{
ESM::Creature data;
std::string baseId;
std::string inventoryBaseId;
std::vector<mwmp::Item> inventory;
BaseOverrides baseOverrides;
};
struct EnchantmentRecord
{
ESM::Enchantment data;
std::string baseId;
BaseOverrides baseOverrides;
};
struct MiscellaneousRecord
{
ESM::Miscellaneous data;
std::string baseId;
BaseOverrides baseOverrides;
};
struct NpcRecord
{
ESM::NPC data;
std::string baseId;
std::string inventoryBaseId;
std::vector<mwmp::Item> inventory;
BaseOverrides baseOverrides;
};
struct PotionRecord
{
ESM::Potion data;
std::string baseId;
BaseOverrides baseOverrides;
};
struct SpellRecord
{
ESM::Spell data;
std::string baseId;
BaseOverrides baseOverrides;
};
struct WeaponRecord
{
ESM::Weapon data;
std::string baseId;
BaseOverrides baseOverrides;
};
static const int maxImageDataSize = 1800;
struct MapTile
@ -60,6 +215,20 @@ namespace mwmp
bool forceWeather;
Weather weather;
unsigned short recordsType;
unsigned int recordsCount;
std::vector<ArmorRecord> armorRecords;
std::vector<BookRecord> bookRecords;
std::vector<ClothingRecord> clothingRecords;
std::vector<CreatureRecord> creatureRecords;
std::vector<EnchantmentRecord> enchantmentRecords;
std::vector<MiscellaneousRecord> miscellaneousRecords;
std::vector<NpcRecord> npcRecords;
std::vector<PotionRecord> potionRecords;
std::vector<SpellRecord> spellRecords;
std::vector<WeaponRecord> weaponRecords;
bool isValid;
};
}

@ -1,5 +1,8 @@
#include "PacketRecordDynamic.hpp"
#include <components/openmw-mp/Log.hpp>
#include <components/openmw-mp/NetworkMessages.hpp>
#include <components/openmw-mp/Utils.hpp>
using namespace mwmp;
@ -13,5 +16,507 @@ void PacketRecordDynamic::Packet(RakNet::BitStream *bs, bool send)
{
WorldstatePacket::Packet(bs, send);
// Placeholder
RW(worldstate->recordsType, send);
if (send)
{
if (worldstate->recordsType == mwmp::RECORD_TYPE::SPELL)
worldstate->recordsCount = Utils::getVectorSize(worldstate->spellRecords);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::POTION)
worldstate->recordsCount = Utils::getVectorSize(worldstate->potionRecords);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::ENCHANTMENT)
worldstate->recordsCount = Utils::getVectorSize(worldstate->enchantmentRecords);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::CREATURE)
worldstate->recordsCount = Utils::getVectorSize(worldstate->creatureRecords);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::NPC)
worldstate->recordsCount = Utils::getVectorSize(worldstate->npcRecords);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::ARMOR)
worldstate->recordsCount = Utils::getVectorSize(worldstate->armorRecords);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::BOOK)
worldstate->recordsCount = Utils::getVectorSize(worldstate->bookRecords);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::CLOTHING)
worldstate->recordsCount = Utils::getVectorSize(worldstate->clothingRecords);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)
worldstate->recordsCount = Utils::getVectorSize(worldstate->miscellaneousRecords);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::WEAPON)
worldstate->recordsCount = Utils::getVectorSize(worldstate->weaponRecords);
else
{
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Processed invalid ID_RECORD_DYNAMIC packet about unimplemented recordsType %i",
worldstate->recordsType);
return;
}
}
RW(worldstate->recordsCount, send);
if (worldstate->recordsCount > maxRecords)
{
LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "Processed invalid ID_RECORD_DYNAMIC packet with %i records, above the maximum of %i",
worldstate->recordsCount, maxRecords);
LOG_APPEND(Log::LOG_ERROR, "- The packet was ignored after that point");
return;
}
if (!send)
{
if (worldstate->recordsType == mwmp::RECORD_TYPE::SPELL)
Utils::resetVector(worldstate->spellRecords, worldstate->recordsCount);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::POTION)
Utils::resetVector(worldstate->potionRecords, worldstate->recordsCount);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::ENCHANTMENT)
Utils::resetVector(worldstate->enchantmentRecords, worldstate->recordsCount);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::CREATURE)
Utils::resetVector(worldstate->creatureRecords, worldstate->recordsCount);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::NPC)
Utils::resetVector(worldstate->npcRecords, worldstate->recordsCount);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::ARMOR)
Utils::resetVector(worldstate->armorRecords, worldstate->recordsCount);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::BOOK)
Utils::resetVector(worldstate->bookRecords, worldstate->recordsCount);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::CLOTHING)
Utils::resetVector(worldstate->clothingRecords, worldstate->recordsCount);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)
Utils::resetVector(worldstate->miscellaneousRecords, worldstate->recordsCount);
else if (worldstate->recordsType == mwmp::RECORD_TYPE::WEAPON)
Utils::resetVector(worldstate->weaponRecords, worldstate->recordsCount);
}
if (worldstate->recordsType == mwmp::RECORD_TYPE::SPELL)
{
for (auto &&record : worldstate->spellRecords)
{
auto &&recordData = record.data;
RW(record.baseId, send, true);
RW(recordData.mId, send, true);
RW(recordData.mName, send, true);
RW(recordData.mData.mType, send);
RW(recordData.mData.mCost, send);
RW(recordData.mData.mFlags, send);
ProcessEffects(recordData.mEffects, send);
if (!record.baseId.empty())
{
auto &&overrides = record.baseOverrides;
RW(overrides.hasName, send);
RW(overrides.hasSubtype, send);
RW(overrides.hasCost, send);
RW(overrides.hasFlags, send);
RW(overrides.hasEffects, send);
}
}
}
else if (worldstate->recordsType == mwmp::RECORD_TYPE::POTION)
{
for (auto &&record : worldstate->potionRecords)
{
auto &recordData = record.data;
RW(record.baseId, send, true);
RW(recordData.mId, send, true);
RW(recordData.mName, send, true);
RW(recordData.mModel, send, true);
RW(recordData.mIcon, send, true);
RW(recordData.mData.mWeight, send);
RW(recordData.mData.mValue, send);
RW(recordData.mData.mAutoCalc, send);
RW(recordData.mScript, send, true);
ProcessEffects(recordData.mEffects, send);
if (!record.baseId.empty())
{
auto &&overrides = record.baseOverrides;
RW(overrides.hasName, send);
RW(overrides.hasModel, send);
RW(overrides.hasIcon, send);
RW(overrides.hasWeight, send);
RW(overrides.hasValue, send);
RW(overrides.hasAutoCalc, send);
RW(overrides.hasScript, send);
RW(overrides.hasEffects, send);
}
}
}
else if (worldstate->recordsType == mwmp::RECORD_TYPE::ENCHANTMENT)
{
for (auto &&record : worldstate->enchantmentRecords)
{
auto &recordData = record.data;
RW(record.baseId, send, true);
RW(recordData.mId, send, true);
RW(recordData.mData.mType, send);
RW(recordData.mData.mCost, send);
RW(recordData.mData.mCharge, send);
RW(recordData.mData.mAutocalc, send);
ProcessEffects(recordData.mEffects, send);
if (!record.baseId.empty())
{
auto &&overrides = record.baseOverrides;
RW(overrides.hasSubtype, send);
RW(overrides.hasCost, send);
RW(overrides.hasCharge, send);
RW(overrides.hasAutoCalc, send);
RW(overrides.hasEffects, send);
}
}
}
else if (worldstate->recordsType == mwmp::RECORD_TYPE::CREATURE)
{
for (auto &&record : worldstate->creatureRecords)
{
auto &recordData = record.data;
RW(record.baseId, send, true);
RW(recordData.mId, send, true);
RW(recordData.mName, send, true);
RW(recordData.mModel, send, true);
RW(recordData.mData.mType, send);
RW(recordData.mData.mLevel, send);
RW(recordData.mFlags, send);
RW(recordData.mScript, send, true);
ProcessInventoryList(record.inventory, recordData.mInventory, send);
if (!record.baseId.empty())
{
auto &&overrides = record.baseOverrides;
RW(overrides.hasName, send);
RW(overrides.hasModel, send);
RW(overrides.hasSubtype, send);
RW(overrides.hasLevel, send);
RW(overrides.hasFlags, send);
RW(overrides.hasScript, send);
RW(overrides.hasInventory, send);
}
}
}
else if (worldstate->recordsType == mwmp::RECORD_TYPE::NPC)
{
for (auto &&record : worldstate->npcRecords)
{
auto &recordData = record.data;
RW(record.baseId, send, true);
RW(record.inventoryBaseId, send, true);
RW(recordData.mId, send, true);
RW(recordData.mName, send, true);
RW(recordData.mFlags, send);
RW(recordData.mRace, send, true);
RW(recordData.mModel, send, true);
RW(recordData.mHair, send, true);
RW(recordData.mHead, send, true);
RW(recordData.mClass, send, true);
RW(recordData.mFaction, send, true);
RW(recordData.mScript, send, true);
RW(recordData.mNpdt.mLevel, send);
RW(recordData.mNpdtType, send);
ProcessInventoryList(record.inventory, recordData.mInventory, send);
if (!record.baseId.empty())
{
auto &&overrides = record.baseOverrides;
RW(overrides.hasName, send);
RW(overrides.hasGender, send);
RW(overrides.hasFlags, send);
RW(overrides.hasModel, send);
RW(overrides.hasHair, send);
RW(overrides.hasHead, send);
RW(overrides.hasFaction, send);
RW(overrides.hasScript, send);
RW(overrides.hasLevel, send);
RW(overrides.hasInventory, send);
}
}
}
else if (worldstate->recordsType == mwmp::RECORD_TYPE::ARMOR)
{
for (auto &&record : worldstate->armorRecords)
{
auto &recordData = record.data;
RW(record.baseId, send, true);
RW(recordData.mId, send, true);
RW(recordData.mName, send, true);
RW(recordData.mModel, send, true);
RW(recordData.mIcon, send, true);
RW(recordData.mData.mType, send);
RW(recordData.mData.mWeight, send);
RW(recordData.mData.mValue, send);
RW(recordData.mData.mHealth, send);
RW(recordData.mData.mArmor, send);
RW(recordData.mData.mEnchant, send);
RW(recordData.mEnchant, send, true);
RW(recordData.mScript, send, true);
ProcessBodyParts(recordData.mParts, send);
if (!record.baseId.empty())
{
auto &&overrides = record.baseOverrides;
RW(overrides.hasName, send);
RW(overrides.hasModel, send);
RW(overrides.hasIcon, send);
RW(overrides.hasSubtype, send);
RW(overrides.hasWeight, send);
RW(overrides.hasValue, send);
RW(overrides.hasHealth, send);
RW(overrides.hasArmorRating, send);
RW(overrides.hasEnchantmentCharge, send);
RW(overrides.hasEnchantmentId, send);
RW(overrides.hasScript, send);
RW(overrides.hasBodyParts, send);
}
}
}
else if (worldstate->recordsType == mwmp::RECORD_TYPE::BOOK)
{
for (auto &&record : worldstate->bookRecords)
{
auto &recordData = record.data;
RW(record.baseId, send, true);
RW(recordData.mId, send, true);
RW(recordData.mName, send, true);
RW(recordData.mModel, send, true);
RW(recordData.mIcon, send, true);
RW(recordData.mText, send, true);
RW(recordData.mData.mWeight, send);
RW(recordData.mData.mValue, send);
RW(recordData.mData.mIsScroll, send);
RW(recordData.mData.mSkillId, send);
RW(recordData.mData.mEnchant, send);
RW(recordData.mEnchant, send, true);
RW(recordData.mScript, send, true);
if (!record.baseId.empty())
{
auto &&overrides = record.baseOverrides;
RW(overrides.hasName, send);
RW(overrides.hasModel, send);
RW(overrides.hasIcon, send);
RW(overrides.hasText, send);
RW(overrides.hasWeight, send);
RW(overrides.hasValue, send);
RW(overrides.hasScrollState, send);
RW(overrides.hasSkillId, send);
RW(overrides.hasEnchantmentCharge, send);
RW(overrides.hasEnchantmentId, send);
RW(overrides.hasScript, send);
}
}
}
else if (worldstate->recordsType == mwmp::RECORD_TYPE::CLOTHING)
{
for (auto &&record : worldstate->clothingRecords)
{
auto &recordData = record.data;
RW(record.baseId, send, true);
RW(recordData.mId, send, true);
RW(recordData.mName, send, true);
RW(recordData.mModel, send, true);
RW(recordData.mIcon, send, true);
RW(recordData.mData.mType, send);
RW(recordData.mData.mWeight, send);
RW(recordData.mData.mValue, send);
RW(recordData.mData.mEnchant, send);
RW(recordData.mEnchant, send, true);
RW(recordData.mScript, send, true);
ProcessBodyParts(recordData.mParts, send);
if (!record.baseId.empty())
{
auto &&overrides = record.baseOverrides;
RW(overrides.hasName, send);
RW(overrides.hasModel, send);
RW(overrides.hasIcon, send);
RW(overrides.hasSubtype, send);
RW(overrides.hasWeight, send);
RW(overrides.hasValue, send);
RW(overrides.hasEnchantmentCharge, send);
RW(overrides.hasEnchantmentId, send);
RW(overrides.hasScript, send);
RW(overrides.hasBodyParts, send);
}
}
}
else if (worldstate->recordsType == mwmp::RECORD_TYPE::MISCELLANEOUS)
{
for (auto &&record : worldstate->miscellaneousRecords)
{
auto &recordData = record.data;
RW(record.baseId, send, true);
RW(recordData.mId, send, true);
RW(recordData.mName, send, true);
RW(recordData.mModel, send, true);
RW(recordData.mIcon, send, true);
RW(recordData.mData.mWeight, send);
RW(recordData.mData.mValue, send);
RW(recordData.mData.mIsKey, send);
RW(recordData.mScript, send, true);
if (!record.baseId.empty())
{
auto &&overrides = record.baseOverrides;
RW(overrides.hasName, send);
RW(overrides.hasModel, send);
RW(overrides.hasIcon, send);
RW(overrides.hasWeight, send);
RW(overrides.hasValue, send);
RW(overrides.hasKeyState, send);
RW(overrides.hasScript, send);
}
}
}
else if (worldstate->recordsType == mwmp::RECORD_TYPE::WEAPON)
{
for (auto &&record : worldstate->weaponRecords)
{
auto &recordData = record.data;
RW(record.baseId, send, true);
RW(recordData.mId, send, true);
RW(recordData.mName, send, true);
RW(recordData.mModel, send, true);
RW(recordData.mIcon, send, true);
RW(recordData.mData.mType, send);
RW(recordData.mData.mWeight, send);
RW(recordData.mData.mValue, send);
RW(recordData.mData.mHealth, send);
RW(recordData.mData.mSpeed, send);
RW(recordData.mData.mReach, send);
RW(recordData.mData.mChop[0], send);
RW(recordData.mData.mChop[1], send);
RW(recordData.mData.mSlash[0], send);
RW(recordData.mData.mSlash[1], send);
RW(recordData.mData.mThrust[0], send);
RW(recordData.mData.mThrust[1], send);
RW(recordData.mData.mFlags, send);
RW(recordData.mData.mEnchant, send);
RW(recordData.mEnchant, send, true);
RW(recordData.mScript, send, true);
if (!record.baseId.empty())
{
auto &&overrides = record.baseOverrides;
RW(overrides.hasName, send);
RW(overrides.hasModel, send);
RW(overrides.hasIcon, send);
RW(overrides.hasSubtype, send);
RW(overrides.hasWeight, send);
RW(overrides.hasValue, send);
RW(overrides.hasHealth, send);
RW(overrides.hasSpeed, send);
RW(overrides.hasReach, send);
RW(overrides.hasDamageChop, send);
RW(overrides.hasDamageSlash, send);
RW(overrides.hasDamageThrust, send);
RW(overrides.hasFlags, send);
RW(overrides.hasEnchantmentCharge, send);
RW(overrides.hasEnchantmentId, send);
RW(overrides.hasScript, send);
}
}
}
}
void PacketRecordDynamic::ProcessEffects(ESM::EffectList &effectList, bool send)
{
uint32_t effectCount;
if (send)
effectCount = static_cast<uint32_t>(effectList.mList.size());
RW(effectCount, send);
if (effectCount > maxEffects)
{
return;
}
if (!send)
{
effectList.mList.clear();
effectList.mList.resize(effectCount);
}
for (auto &&effect : effectList.mList)
{
RW(effect.mEffectID, send);
RW(effect.mAttribute, send);
RW(effect.mSkill, send);
RW(effect.mRange, send);
RW(effect.mArea, send);
RW(effect.mDuration, send);
RW(effect.mMagnMax, send);
RW(effect.mMagnMin, send);
}
}
void PacketRecordDynamic::ProcessBodyParts(ESM::PartReferenceList &partList, bool send)
{
uint32_t partCount;
if (send)
partCount = static_cast<uint32_t>(partList.mParts.size());
RW(partCount, send);
if (partCount > maxParts)
{
return;
}
if (!send)
{
partList.mParts.clear();
partList.mParts.resize(partCount);
}
for (auto &&part : partList.mParts)
{
RW(part.mPart, send);
RW(part.mMale, send, true);
RW(part.mFemale, send, true);
}
}
// ESM::InventoryList has a strange structure that makes it hard to read in packets directly, so we just deal with it
// here with the help of a separate mwmp::Item vector
void PacketRecordDynamic::ProcessInventoryList(std::vector<mwmp::Item> &inventory, ESM::InventoryList &inventoryList, bool send)
{
uint32_t itemCount;
if (send)
itemCount = static_cast<uint32_t>(inventory.size());
RW(itemCount, send);
if (itemCount > maxItems)
{
return;
}
if (!send)
{
inventory.clear();
inventory.resize(itemCount);
inventoryList.mList.clear();
}
for (auto &&item : inventory)
{
RW(item.refId, send, true);
RW(item.count, send, true);
if (!send)
{
ESM::ContItem contItem;
contItem.mItem.assign(item.refId);
contItem.mCount = item.count;
inventoryList.mList.push_back(contItem);
}
}
}

@ -12,6 +12,16 @@ namespace mwmp
PacketRecordDynamic(RakNet::RakPeerInterface *peer);
virtual void Packet(RakNet::BitStream *bs, bool send);
void ProcessEffects(ESM::EffectList &effectList, bool send);
void ProcessBodyParts(ESM::PartReferenceList &bodyPartList, bool send);
void ProcessInventoryList(std::vector<mwmp::Item> &inventory, ESM::InventoryList &inventoryList, bool send);
protected:
static const int maxRecords = 3000;
static const int maxEffects = 100;
static const int maxParts = 7;
static const int maxItems = 1000;
};
}

Loading…
Cancel
Save