Server upload

RD Core / Commit #2

Commit Server upload

Alex Alex committed 18/10/2026 23:38 main Patch
1,106 files +223,404 -0
A src/movement.h +256-0 View file
@@ -0,0 +1,256 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#ifndef FS_MOVEMENT_H_5E0D2626D4634ACA83AC6509518E5F49
21+#define FS_MOVEMENT_H_5E0D2626D4634ACA83AC6509518E5F49
22+
23+#include "baseevents.h"
24+#include "item.h"
25+#include "luascript.h"
26+#include "vocation.h"
27+
28+extern Vocations g_vocations;
29+
30+enum MoveEvent_t {
31+ MOVE_EVENT_STEP_IN,
32+ MOVE_EVENT_STEP_OUT,
33+ MOVE_EVENT_EQUIP,
34+ MOVE_EVENT_DEEQUIP,
35+ MOVE_EVENT_ADD_ITEM,
36+ MOVE_EVENT_REMOVE_ITEM,
37+ MOVE_EVENT_ADD_ITEM_ITEMTILE,
38+ MOVE_EVENT_REMOVE_ITEM_ITEMTILE,
39+
40+ MOVE_EVENT_LAST,
41+ MOVE_EVENT_NONE
42+};
43+
44+class MoveEvent;
45+using MoveEvent_ptr = std::unique_ptr<MoveEvent>;
46+
47+struct MoveEventList {
48+ std::list<MoveEvent> moveEvent[MOVE_EVENT_LAST];
49+};
50+
51+using VocEquipMap = std::map<uint16_t, bool>;
52+
53+class MoveEvents final : public BaseEvents
54+{
55+ public:
56+ MoveEvents();
57+ ~MoveEvents();
58+
59+ // non-copyable
60+ MoveEvents(const MoveEvents&) = delete;
61+ MoveEvents& operator=(const MoveEvents&) = delete;
62+
63+ uint32_t onCreatureMove(Creature* creature, const Tile* tile, MoveEvent_t eventType);
64+ ReturnValue onPlayerEquip(Player* player, Item* item, slots_t slot, bool isCheck);
65+ ReturnValue onPlayerDeEquip(Player* player, Item* item, slots_t slot);
66+ uint32_t onItemMove(Item* item, Tile* tile, bool isAdd);
67+
68+ MoveEvent* getEvent(Item* item, MoveEvent_t eventType);
69+
70+ bool registerLuaEvent(MoveEvent* event);
71+ bool registerLuaFunction(MoveEvent* event);
72+ void clear(bool fromLua) override final;
73+
74+ private:
75+ using MoveListMap = std::map<int32_t, MoveEventList>;
76+ using MovePosListMap = std::map<Position, MoveEventList>;
77+ void clearMap(MoveListMap& map, bool fromLua);
78+ void clearPosMap(MovePosListMap& map, bool fromLua);
79+
80+ LuaScriptInterface& getScriptInterface() override;
81+ std::string getScriptBaseName() const override;
82+ Event_ptr getEvent(const std::string& nodeName) override;
83+ bool registerEvent(Event_ptr event, const pugi::xml_node& node) override;
84+
85+ void addEvent(MoveEvent moveEvent, int32_t id, MoveListMap& map);
86+
87+ void addEvent(MoveEvent moveEvent, const Position& pos, MovePosListMap& map);
88+ MoveEvent* getEvent(const Tile* tile, MoveEvent_t eventType);
89+
90+ MoveEvent* getEvent(Item* item, MoveEvent_t eventType, slots_t slot);
91+
92+ MoveListMap uniqueIdMap;
93+ MoveListMap actionIdMap;
94+ MoveListMap itemIdMap;
95+ MovePosListMap positionMap;
96+
97+ LuaScriptInterface scriptInterface;
98+};
99+
100+using StepFunction = std::function<uint32_t(Creature* creature, Item* item, const Position& pos)>;
101+using MoveFunction = std::function<uint32_t(Item* item, Item* tileItem, const Position& pos)>;
102+using EquipFunction = std::function<ReturnValue(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean)>;
103+
104+class MoveEvent final : public Event
105+{
106+ public:
107+ explicit MoveEvent(LuaScriptInterface* interface);
108+
109+ MoveEvent_t getEventType() const;
110+ void setEventType(MoveEvent_t type);
111+
112+ bool configureEvent(const pugi::xml_node& node) override;
113+ bool loadFunction(const pugi::xml_attribute& attr, bool isScripted) override;
114+
115+ uint32_t fireStepEvent(Creature* creature, Item* item, const Position& pos);
116+ uint32_t fireAddRemItem(Item* item, Item* tileItem, const Position& pos);
117+ ReturnValue fireEquip(Player* player, Item* item, slots_t slot, bool isCheck);
118+
119+ uint32_t getSlot() const {
120+ return slot;
121+ }
122+
123+ //scripting
124+ bool executeStep(Creature* creature, Item* item, const Position& pos);
125+ bool executeEquip(Player* player, Item* item, slots_t slot, bool isCheck);
126+ bool executeAddRemItem(Item* item, Item* tileItem, const Position& pos);
127+ //
128+
129+ //onEquip information
130+ uint32_t getReqLevel() const {
131+ return reqLevel;
132+ }
133+ uint32_t getReqMagLv() const {
134+ return reqMagLevel;
135+ }
136+ bool isPremium() const {
137+ return premium;
138+ }
139+ const std::string& getVocationString() const {
140+ return vocationString;
141+ }
142+ void setVocationString(const std::string& str) {
143+ vocationString = str;
144+ }
145+ uint32_t getWieldInfo() const {
146+ return wieldInfo;
147+ }
148+ const VocEquipMap& getVocEquipMap() const {
149+ return vocEquipMap;
150+ }
151+ void addVocEquipMap(std::string vocName) {
152+ int32_t vocationId = g_vocations.getVocationId(vocName);
153+ if (vocationId != -1) {
154+ vocEquipMap[vocationId] = true;
155+ }
156+ }
157+ bool getTileItem() const {
158+ return tileItem;
159+ }
160+ void setTileItem(bool b) {
161+ tileItem = b;
162+ }
163+ std::vector<uint32_t> getItemIdRange() {
164+ return itemIdRange;
165+ }
166+ void addItemId(uint32_t id) {
167+ itemIdRange.emplace_back(id);
168+ }
169+ std::vector<uint32_t> getActionIdRange() {
170+ return actionIdRange;
171+ }
172+ void addActionId(uint32_t id) {
173+ actionIdRange.emplace_back(id);
174+ }
175+ std::vector<uint32_t> getUniqueIdRange() {
176+ return uniqueIdRange;
177+ }
178+ void addUniqueId(uint32_t id) {
179+ uniqueIdRange.emplace_back(id);
180+ }
181+ std::vector<Position> getPosList() {
182+ return posList;
183+ }
184+ void addPosList(Position pos) {
185+ posList.emplace_back(pos);
186+ }
187+ std::string getSlotName() {
188+ return slotName;
189+ }
190+ void setSlotName(std::string name) {
191+ slotName = name;
192+ }
193+ void setSlot(uint32_t s) {
194+ slot = s;
195+ }
196+ uint32_t getRequiredLevel() {
197+ return reqLevel;
198+ }
199+ void setRequiredLevel(uint32_t level) {
200+ reqLevel = level;
201+ }
202+ uint32_t getRequiredMagLevel() {
203+ return reqMagLevel;
204+ }
205+ void setRequiredMagLevel(uint32_t level) {
206+ reqMagLevel = level;
207+ }
208+ bool needPremium() {
209+ return premium;
210+ }
211+ void setNeedPremium(bool b) {
212+ premium = b;
213+ }
214+ uint32_t getWieldInfo() {
215+ return wieldInfo;
216+ }
217+ void setWieldInfo(WieldInfo_t info) {
218+ wieldInfo |= info;
219+ }
220+
221+ static uint32_t StepInField(Creature* creature, Item* item, const Position& pos);
222+ static uint32_t StepOutField(Creature* creature, Item* item, const Position& pos);
223+
224+ static uint32_t AddItemField(Item* item, Item* tileItem, const Position& pos);
225+ static uint32_t RemoveItemField(Item* item, Item* tileItem, const Position& pos);
226+
227+ static ReturnValue EquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean);
228+ static ReturnValue DeEquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean);
229+
230+ MoveEvent_t eventType = MOVE_EVENT_NONE;
231+ StepFunction stepFunction;
232+ MoveFunction moveFunction;
233+ EquipFunction equipFunction;
234+
235+ private:
236+ std::string getScriptEventName() const override;
237+
238+ uint32_t slot = SLOTP_WHEREEVER;
239+ std::string slotName;
240+
241+ //onEquip information
242+ uint32_t reqLevel = 0;
243+ uint32_t reqMagLevel = 0;
244+ bool premium = false;
245+ std::string vocationString;
246+ uint32_t wieldInfo = 0;
247+ VocEquipMap vocEquipMap;
248+ bool tileItem = false;
249+
250+ std::vector<uint32_t> itemIdRange;
251+ std::vector<uint32_t> actionIdRange;
252+ std::vector<uint32_t> uniqueIdRange;
253+ std::vector<Position> posList;
254+};
255+
256+#endif
A src/networkmessage.cpp +164-0 View file
@@ -0,0 +1,164 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#include "otpch.h"
21+
22+#include "networkmessage.h"
23+
24+#include "container.h"
25+#include "creature.h"
26+
27+#include "game.h"
28+
29+extern Game g_game;
30+
31+std::string NetworkMessage::getString(uint16_t stringLen/* = 0*/)
32+{
33+ if (stringLen == 0) {
34+ stringLen = get<uint16_t>();
35+ }
36+
37+ if (!canRead(stringLen)) {
38+ return std::string();
39+ }
40+
41+ char* v = reinterpret_cast<char*>(buffer) + info.position; //does not break strict aliasing
42+ info.position += stringLen;
43+ return std::string(v, stringLen);
44+}
45+
46+Position NetworkMessage::getPosition()
47+{
48+ Position pos;
49+ pos.x = get<uint16_t>();
50+ pos.y = get<uint16_t>();
51+ pos.z = getByte();
52+ return pos;
53+}
54+
55+void NetworkMessage::addString(const std::string& value)
56+{
57+ size_t stringLen = value.length();
58+ if (!canAdd(stringLen + 2)) {
59+ return;
60+ }
61+
62+ add<uint16_t>(stringLen);
63+ memcpy(buffer + info.position, value.c_str(), stringLen);
64+ info.position += stringLen;
65+ info.length += stringLen;
66+}
67+
68+void NetworkMessage::addDouble(double value, uint8_t precision/* = 2*/)
69+{
70+ addByte(precision);
71+ add<uint32_t>(static_cast<uint32_t>((value * std::pow(static_cast<float>(10), precision)) + std::numeric_limits<int32_t>::max()));
72+}
73+
74+void NetworkMessage::addBytes(const char* bytes, size_t size)
75+{
76+ if (!canAdd(size)) {
77+ return;
78+ }
79+
80+ memcpy(buffer + info.position, bytes, size);
81+ info.position += size;
82+ info.length += size;
83+}
84+
85+void NetworkMessage::addPaddingBytes(size_t n)
86+{
87+ if (!canAdd(n)) {
88+ return;
89+ }
90+
91+ memset(buffer + info.position, 0x33, n);
92+ info.length += n;
93+}
94+
95+void NetworkMessage::addPosition(const Position& pos)
96+{
97+ add<uint16_t>(pos.x);
98+ add<uint16_t>(pos.y);
99+ addByte(pos.z);
100+}
101+
102+void NetworkMessage::addItem(uint16_t id, uint8_t count, bool withDescription)
103+{
104+ const ItemType& it = Item::items[id];
105+
106+ add<uint16_t>(it.clientId);
107+
108+ addByte(0xFF); // MARK_UNMARKED
109+
110+ if (it.stackable) {
111+ addByte(count);
112+ } else if (it.isSplash() || it.isFluidContainer()) {
113+ addByte(fluidMap[count & 7]);
114+ }
115+
116+ if (it.isAnimation) {
117+ addByte(0xFE); // random phase (0xFF for async)
118+ }
119+
120+ if (withDescription) {
121+ addString("");
122+ }
123+}
124+
125+void NetworkMessage::addItem(const Item* item, bool withDescription)
126+{
127+ const ItemType& it = Item::items[item->getID()];
128+
129+ add<uint16_t>(it.clientId);
130+ addByte(0xFF); // MARK_UNMARKED
131+
132+ if (it.stackable) {
133+ addByte(std::min<uint16_t>(0xFF, item->getItemCount()));
134+ } else if (it.isSplash() || it.isFluidContainer()) {
135+ addByte(fluidMap[item->getFluidType() & 7]);
136+ }
137+
138+ if (it.isAnimation) {
139+ addByte(0xFE); // random phase (0xFF for async)
140+ }
141+
142+ if (withDescription) {
143+ addString(item->getDescription(0));
144+ }
145+}
146+
147+void NetworkMessage::addItemId(uint16_t itemId)
148+{
149+ add<uint16_t>(Item::items[itemId].clientId);
150+}
151+
152+void NetworkMessage::addOutfit(Outfit_t& outfit)
153+{
154+ add<uint16_t>(outfit.lookType);
155+ add<uint16_t>(outfit.lookHead);
156+ add<uint16_t>(outfit.lookBody);
157+ add<uint16_t>(outfit.lookLegs);
158+ add<uint16_t>(outfit.lookFeet);
159+ add<uint16_t>(outfit.lookAddons);
160+ add<uint16_t>(outfit.lookWings);
161+ add<uint16_t>(outfit.lookAura);
162+
163+ addString(g_game.shaders.getShaderNameByID(outfit.lookShader));
164+}
A src/networkmessage.h +178-0 View file
@@ -0,0 +1,178 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#ifndef FS_NETWORKMESSAGE_H_B853CFED58D1413A87ACED07B2926E03
21+#define FS_NETWORKMESSAGE_H_B853CFED58D1413A87ACED07B2926E03
22+
23+#include "const.h"
24+#include "enums.h"
25+
26+class Item;
27+class Creature;
28+class Player;
29+struct Position;
30+class RSA;
31+
32+class NetworkMessage
33+{
34+ public:
35+ using MsgSize_t = uint16_t;
36+ // Headers:
37+ // 2 bytes for unencrypted message size
38+ // 4 bytes for checksum
39+ // 2 bytes for encrypted message size
40+ static constexpr MsgSize_t INITIAL_BUFFER_POSITION = 8;
41+ enum { HEADER_LENGTH = 2 };
42+ enum { CHECKSUM_LENGTH = 4 };
43+ enum { XTEA_MULTIPLE = 8 };
44+ enum { MAX_BODY_LENGTH = NETWORKMESSAGE_MAXSIZE - HEADER_LENGTH - CHECKSUM_LENGTH - XTEA_MULTIPLE };
45+ enum { MAX_PROTOCOL_BODY_LENGTH = MAX_BODY_LENGTH - 10 };
46+
47+ NetworkMessage() = default;
48+
49+ void reset() {
50+ info = {};
51+ }
52+
53+ // simply read functions for incoming message
54+ uint8_t getByte() {
55+ if (!canRead(1)) {
56+ return 0;
57+ }
58+
59+ return buffer[info.position++];
60+ }
61+
62+ uint8_t getPreviousByte() {
63+ return buffer[--info.position];
64+ }
65+
66+ template<typename T>
67+ T get() {
68+ if (!canRead(sizeof(T))) {
69+ return 0;
70+ }
71+
72+ T v;
73+ memcpy(&v, buffer + info.position, sizeof(T));
74+ info.position += sizeof(T);
75+ return v;
76+ }
77+
78+ std::string getString(uint16_t stringLen = 0);
79+ Position getPosition();
80+
81+ // skips count unknown/unused bytes in an incoming message
82+ void skipBytes(int16_t count) {
83+ info.position += count;
84+ }
85+
86+ // simply write functions for outgoing message
87+ void addByte(uint8_t value) {
88+ if (!canAdd(1)) {
89+ return;
90+ }
91+
92+ buffer[info.position++] = value;
93+ info.length++;
94+ }
95+
96+ template<typename T>
97+ void add(T value) {
98+ if (!canAdd(sizeof(T))) {
99+ return;
100+ }
101+
102+ memcpy(buffer + info.position, &value, sizeof(T));
103+ info.position += sizeof(T);
104+ info.length += sizeof(T);
105+ }
106+
107+ void addBytes(const char* bytes, size_t size);
108+ void addPaddingBytes(size_t n);
109+
110+ void addString(const std::string& value);
111+
112+ void addDouble(double value, uint8_t precision = 2);
113+
114+ // write functions for complex types
115+ void addPosition(const Position& pos);
116+ void addItem(uint16_t id, uint8_t count, bool withDescription = false);
117+ void addItem(const Item* item, bool withDescription = false);
118+ void addItemId(uint16_t itemId);
119+ void addOutfit(Outfit_t& outfit);
120+
121+ MsgSize_t getLength() const {
122+ return info.length;
123+ }
124+
125+ void setLength(MsgSize_t newLength) {
126+ info.length = newLength;
127+ }
128+
129+ MsgSize_t getBufferPosition() const {
130+ return info.position;
131+ }
132+
133+ uint16_t getLengthHeader() const {
134+ return static_cast<uint16_t>(buffer[0] | buffer[1] << 8);
135+ }
136+
137+ bool isOverrun() const {
138+ return info.overrun;
139+ }
140+
141+ uint8_t* getBuffer() {
142+ return buffer;
143+ }
144+
145+ const uint8_t* getBuffer() const {
146+ return buffer;
147+ }
148+
149+ uint8_t* getBodyBuffer() {
150+ info.position = 2;
151+ return buffer + HEADER_LENGTH;
152+ }
153+
154+ protected:
155+ struct NetworkMessageInfo {
156+ MsgSize_t length = 0;
157+ MsgSize_t position = INITIAL_BUFFER_POSITION;
158+ bool overrun = false;
159+ };
160+
161+ NetworkMessageInfo info;
162+ uint8_t buffer[NETWORKMESSAGE_MAXSIZE];
163+
164+ private:
165+ bool canAdd(size_t size) const {
166+ return (size + info.position) < MAX_BODY_LENGTH;
167+ }
168+
169+ bool canRead(int32_t size) {
170+ if ((info.position + size) > (info.length + 8) || size >= (NETWORKMESSAGE_MAXSIZE - info.position)) {
171+ info.overrun = true;
172+ return false;
173+ }
174+ return true;
175+ }
176+};
177+
178+#endif // #ifndef __NETWORK_MESSAGE_H__
A src/npc.h +255-0 View file
@@ -0,0 +1,255 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#ifndef FS_NPC_H_B090D0CB549D4435AFA03647195D156F
21+#define FS_NPC_H_B090D0CB549D4435AFA03647195D156F
22+
23+#include "creature.h"
24+#include "luascript.h"
25+
26+#include <set>
27+
28+class Npc;
29+class Player;
30+
31+class Npcs
32+{
33+ public:
34+ static void reload();
35+};
36+
37+class NpcScriptInterface final : public LuaScriptInterface
38+{
39+ public:
40+ NpcScriptInterface();
41+
42+ bool loadNpcLib(const std::string& file);
43+
44+ private:
45+ void registerFunctions();
46+
47+ static int luaActionSay(lua_State* L);
48+ static int luaActionMove(lua_State* L);
49+ static int luaActionMoveTo(lua_State* L);
50+ static int luaActionTurn(lua_State* L);
51+ static int luaActionFollow(lua_State* L);
52+ static int luagetDistanceTo(lua_State* L);
53+ static int luaSetNpcFocus(lua_State* L);
54+ static int luaGetNpcCid(lua_State* L);
55+ static int luaGetNpcParameter(lua_State* L);
56+ static int luaOpenShopWindow(lua_State* L);
57+ static int luaCloseShopWindow(lua_State* L);
58+ static int luaDoSellItem(lua_State* L);
59+
60+ // metatable
61+ static int luaNpcGetParameter(lua_State* L);
62+ static int luaNpcSetFocus(lua_State* L);
63+
64+ static int luaNpcOpenShopWindow(lua_State* L);
65+ static int luaNpcCloseShopWindow(lua_State* L);
66+
67+ private:
68+ bool initState() override;
69+ bool closeState() override;
70+
71+ bool libLoaded;
72+};
73+
74+class NpcEventsHandler
75+{
76+ public:
77+ NpcEventsHandler(const std::string& file, Npc* npc);
78+
79+ void onCreatureAppear(Creature* creature);
80+ void onCreatureDisappear(Creature* creature);
81+ void onCreatureMove(Creature* creature, const Position& oldPos, const Position& newPos);
82+ void onCreatureSay(Creature* creature, SpeakClasses, const std::string& text);
83+ void onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, uint8_t amount, bool ignore = false, bool inBackpacks = false);
84+ void onPlayerCloseChannel(Player* player);
85+ void onPlayerEndTrade(Player* player);
86+ void onThink();
87+
88+ bool isLoaded() const;
89+
90+ private:
91+ Npc* npc;
92+ NpcScriptInterface* scriptInterface;
93+
94+ int32_t creatureAppearEvent = -1;
95+ int32_t creatureDisappearEvent = -1;
96+ int32_t creatureMoveEvent = -1;
97+ int32_t creatureSayEvent = -1;
98+ int32_t playerCloseChannelEvent = -1;
99+ int32_t playerEndTradeEvent = -1;
100+ int32_t thinkEvent = -1;
101+ bool loaded = false;
102+};
103+
104+class Npc final : public Creature
105+{
106+ public:
107+ ~Npc();
108+
109+ // non-copyable
110+ Npc(const Npc&) = delete;
111+ Npc& operator=(const Npc&) = delete;
112+
113+ Npc* getNpc() override {
114+ return this;
115+ }
116+ const Npc* getNpc() const override {
117+ return this;
118+ }
119+
120+ bool isPushable() const override {
121+ return pushable && walkTicks != 0;
122+ }
123+
124+ void setID() override {
125+ if (id == 0) {
126+ id = npcAutoID++;
127+ }
128+ }
129+
130+ void removeList() override;
131+ void addList() override;
132+
133+ static Npc* createNpc(const std::string& name);
134+
135+ bool canSee(const Position& pos) const override;
136+
137+ bool load();
138+ void reload();
139+
140+ const std::string& getName() const override {
141+ return name;
142+ }
143+ const std::string& getNameDescription() const override {
144+ return name;
145+ }
146+
147+ CreatureType_t getType() const override {
148+ return CREATURETYPE_NPC;
149+ }
150+
151+ uint8_t getSpeechBubble() const override {
152+ return speechBubble;
153+ }
154+ void setSpeechBubble(const uint8_t bubble) {
155+ speechBubble = bubble;
156+ }
157+
158+ void doSay(const std::string& text);
159+ void doSayToPlayer(Player* player, const std::string& text);
160+
161+ void doMoveTo(const Position& pos);
162+
163+ int32_t getMasterRadius() const {
164+ return masterRadius;
165+ }
166+ const Position& getMasterPos() const {
167+ return masterPos;
168+ }
169+ void setMasterPos(Position pos, int32_t radius = 1) {
170+ masterPos = pos;
171+ if (masterRadius == -1) {
172+ masterRadius = radius;
173+ }
174+ }
175+
176+ void onPlayerCloseChannel(Player* player);
177+ void onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count,
178+ uint8_t amount, bool ignore = false, bool inBackpacks = false);
179+ void onPlayerEndTrade(Player* player, int32_t buyCallback, int32_t sellCallback);
180+
181+ void turnToCreature(Creature* creature);
182+ void setCreatureFocus(Creature* creature);
183+
184+ NpcScriptInterface* getScriptInterface();
185+
186+ static uint32_t npcAutoID;
187+
188+ private:
189+ explicit Npc(const std::string& name);
190+
191+ void onCreatureAppear(Creature* creature, bool isLogin) override;
192+ void onRemoveCreature(Creature* creature, bool isLogout) override;
193+ void onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos,
194+ const Tile* oldTile, const Position& oldPos, bool teleport) override;
195+
196+ void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) override;
197+ void onThink(uint32_t interval) override;
198+ std::string getDescription(int32_t lookDistance) const override;
199+
200+ bool isImmune(CombatType_t) const override {
201+ return !attackable;
202+ }
203+ bool isImmune(ConditionType_t) const override {
204+ return !attackable;
205+ }
206+ bool isAttackable() const override {
207+ return attackable;
208+ }
209+ bool getNextStep(Direction& dir, uint32_t& flags) override;
210+
211+ void setIdle(bool idle);
212+ void updateIdleStatus();
213+
214+ bool canWalkTo(const Position& fromPos, Direction dir) const;
215+ bool getRandomStep(Direction& dir) const;
216+
217+ void reset();
218+ bool loadFromXml();
219+
220+ void addShopPlayer(Player* player);
221+ void removeShopPlayer(Player* player);
222+ void closeAllShopWindows();
223+
224+ std::map<std::string, std::string> parameters;
225+
226+ std::set<Player*> shopPlayerSet;
227+ std::set<Player*> spectators;
228+
229+ std::string name;
230+ std::string filename;
231+
232+ NpcEventsHandler* npcEventHandler;
233+
234+ Position masterPos;
235+
236+ uint32_t walkTicks;
237+ int32_t focusCreature;
238+ int32_t masterRadius;
239+
240+ uint8_t speechBubble;
241+
242+ bool floorChange;
243+ bool attackable;
244+ bool ignoreHeight;
245+ bool loaded;
246+ bool isIdle;
247+ bool pushable;
248+
249+ static NpcScriptInterface* scriptInterface;
250+
251+ friend class Npcs;
252+ friend class NpcScriptInterface;
253+};
254+
255+#endif
A src/otpch.cpp +20-0 View file
@@ -0,0 +1,20 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#include "otpch.h"
A src/otpch.h +44-0 View file
@@ -0,0 +1,44 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#define FS_OTPCH_H_F00C737DA6CA4C8D90F57430C614367F
21+
22+// Definitions should be global.
23+#include "definitions.h"
24+
25+#include <algorithm>
26+#include <chrono>
27+#include <cstdint>
28+#include <forward_list>
29+#include <functional>
30+#include <iomanip>
31+#include <iostream>
32+#include <list>
33+#include <map>
34+#include <memory>
35+#include <mutex>
36+#include <sstream>
37+#include <string>
38+#include <thread>
39+#include <unordered_map>
40+#include <vector>
41+
42+#include <boost/asio.hpp>
43+
44+#include <pugixml.hpp>
A src/otserv.cpp +325-0 View file
@@ -0,0 +1,325 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#include "otpch.h"
21+
22+#include "server.h"
23+
24+#include "game.h"
25+
26+#include "iomarket.h"
27+
28+#include "configmanager.h"
29+#include "scriptmanager.h"
30+#include "rsa.h"
31+#include "protocolold.h"
32+#include "protocollogin.h"
33+#include "protocolstatus.h"
34+#include "databasemanager.h"
35+#include "scheduler.h"
36+#include "databasetasks.h"
37+#include "script.h"
38+#include <fstream>
39+#if __has_include("gitmetadata.h")
40+ #include "gitmetadata.h"
41+#endif
42+
43+DatabaseTasks g_databaseTasks;
44+Dispatcher g_dispatcher;
45+Scheduler g_scheduler;
46+
47+Game g_game;
48+ConfigManager g_config;
49+Monsters g_monsters;
50+Vocations g_vocations;
51+extern Scripts* g_scripts;
52+RSA g_RSA;
53+
54+std::mutex g_loaderLock;
55+std::condition_variable g_loaderSignal;
56+std::unique_lock<std::mutex> g_loaderUniqueLock(g_loaderLock);
57+
58+void startupErrorMessage(const std::string& errorStr)
59+{
60+ std::cout << "> ERROR: " << errorStr << std::endl;
61+ g_loaderSignal.notify_all();
62+}
63+
64+void mainLoader(int argc, char* argv[], ServiceManager* services);
65+
66+[[noreturn]] void badAllocationHandler()
67+{
68+ // Use functions that only use stack allocation
69+ puts("Allocation failed, server out of memory.\nDecrease the size of your map or compile in 64 bits mode.\n");
70+ getchar();
71+ exit(-1);
72+}
73+
74+int main(int argc, char* argv[])
75+{
76+ // Setup bad allocation handler
77+ std::set_new_handler(badAllocationHandler);
78+
79+ ServiceManager serviceManager;
80+
81+ g_dispatcher.start();
82+ g_scheduler.start();
83+
84+ g_dispatcher.addTask(createTask(std::bind(mainLoader, argc, argv, &serviceManager)));
85+
86+ g_loaderSignal.wait(g_loaderUniqueLock);
87+
88+ if (serviceManager.is_running()) {
89+ std::cout << ">> " << g_config.getString(ConfigManager::SERVER_NAME) << " Server Online!" << std::endl << std::endl;
90+ serviceManager.run();
91+ } else {
92+ std::cout << ">> No services running. The server is NOT online." << std::endl;
93+ g_scheduler.shutdown();
94+ g_databaseTasks.shutdown();
95+ g_dispatcher.shutdown();
96+ }
97+
98+ g_scheduler.join();
99+ g_databaseTasks.join();
100+ g_dispatcher.join();
101+ return 0;
102+}
103+
104+void mainLoader(int, char*[], ServiceManager* services)
105+{
106+ //dispatcher thread
107+ g_game.setGameState(GAME_STATE_STARTUP);
108+
109+ srand(static_cast<unsigned int>(OTSYS_TIME()));
110+#ifdef _WIN32
111+ SetConsoleTitle(STATUS_SERVER_NAME);
112+#endif
113+#if defined(GIT_RETRIEVED_STATE) && GIT_RETRIEVED_STATE
114+ std::cout << STATUS_SERVER_NAME << " - Version " << GIT_DESCRIBE << std::endl;
115+ std::cout << "Git SHA1 " << GIT_SHORT_SHA1 << " dated " << GIT_COMMIT_DATE_ISO8601 << std::endl;
116+ #if GIT_IS_DIRTY
117+ std::cout << "*** DIRTY - NOT OFFICIAL RELEASE ***" << std::endl;
118+ #endif
119+#else
120+ std::cout << STATUS_SERVER_NAME << " - Version " << STATUS_SERVER_VERSION << std::endl;
121+#endif
122+ std::cout << std::endl;
123+
124+ std::cout << "Compiled with " << BOOST_COMPILER << std::endl;
125+ std::cout << "Compiled on " << __DATE__ << ' ' << __TIME__ << " for platform ";
126+#if defined(__amd64__) || defined(_M_X64)
127+ std::cout << "x64" << std::endl;
128+#elif defined(__i386__) || defined(_M_IX86) || defined(_X86_)
129+ std::cout << "x86" << std::endl;
130+#elif defined(__arm__)
131+ std::cout << "ARM" << std::endl;
132+#else
133+ std::cout << "unknown" << std::endl;
134+#endif
135+#if defined(LUAJIT_VERSION)
136+ std::cout << "Linked with " << LUAJIT_VERSION << " for Lua support" << std::endl;
137+#else
138+ std::cout << "Linked with " << LUA_RELEASE << " for Lua support" << std::endl;
139+#endif
140+ std::cout << std::endl;
141+
142+ std::cout << "A server developed by " << STATUS_SERVER_DEVELOPERS << std::endl;
143+ std::cout << std::endl;
144+
145+ // check if config.lua or config.lua.dist exist
146+ std::ifstream c_test("./config.lua");
147+ if (!c_test.is_open()) {
148+ std::ifstream config_lua_dist("./config.lua.dist");
149+ if (config_lua_dist.is_open()) {
150+ std::cout << ">> copying config.lua.dist to config.lua" << std::endl;
151+ std::ofstream config_lua("config.lua");
152+ config_lua << config_lua_dist.rdbuf();
153+ config_lua.close();
154+ config_lua_dist.close();
155+ }
156+ } else {
157+ c_test.close();
158+ }
159+
160+ // read global config
161+ std::cout << ">> Loading config" << std::endl;
162+ if (!g_config.load()) {
163+ startupErrorMessage("Unable to load config.lua!");
164+ return;
165+ }
166+
167+#ifdef _WIN32
168+ const std::string& defaultPriority = g_config.getString(ConfigManager::DEFAULT_PRIORITY);
169+ if (strcasecmp(defaultPriority.c_str(), "high") == 0) {
170+ SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
171+ } else if (strcasecmp(defaultPriority.c_str(), "above-normal") == 0) {
172+ SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS);
173+ }
174+#endif
175+
176+ //set RSA key
177+ try {
178+ g_RSA.loadPEM("key.pem");
179+ } catch(const std::exception& e) {
180+ startupErrorMessage(e.what());
181+ return;
182+ }
183+
184+ std::cout << ">> Establishing database connection..." << std::flush;
185+
186+ if (!Database::getInstance().connect()) {
187+ startupErrorMessage("Failed to connect to database.");
188+ return;
189+ }
190+
191+ std::cout << " MySQL " << Database::getClientVersion() << std::endl;
192+
193+ // run database manager
194+ std::cout << ">> Running database manager" << std::endl;
195+
196+ if (!DatabaseManager::isDatabaseSetup()) {
197+ startupErrorMessage("The database you have specified in config.lua is empty, please import the schema.sql to your database.");
198+ return;
199+ }
200+ g_databaseTasks.start();
201+
202+ DatabaseManager::updateDatabase();
203+
204+ if (g_config.getBoolean(ConfigManager::OPTIMIZE_DATABASE) && !DatabaseManager::optimizeTables()) {
205+ std::cout << "> No tables were optimized." << std::endl;
206+ }
207+
208+ //load vocations
209+ std::cout << ">> Loading vocations" << std::endl;
210+ if (!g_vocations.loadFromXml()) {
211+ startupErrorMessage("Unable to load vocations!");
212+ return;
213+ }
214+
215+ // load item data
216+ std::cout << ">> Loading items" << std::endl;
217+ if (!Item::items.loadFromOtb("data/items/items.otb")) {
218+ startupErrorMessage("Unable to load items (OTB)!");
219+ return;
220+ }
221+
222+ if (!Item::items.loadFromXml()) {
223+ startupErrorMessage("Unable to load items (XML)!");
224+ return;
225+ }
226+
227+ std::cout << ">> Loading script systems" << std::endl;
228+ if (!ScriptingManager::getInstance().loadScriptSystems()) {
229+ startupErrorMessage("Failed to load script systems");
230+ return;
231+ }
232+
233+ std::cout << ">> Loading lua scripts" << std::endl;
234+ if (!g_scripts->loadScripts("scripts", false, false)) {
235+ startupErrorMessage("Failed to load lua scripts");
236+ return;
237+ }
238+
239+ std::cout << ">> Loading monsters" << std::endl;
240+ if (!g_monsters.loadFromXml()) {
241+ startupErrorMessage("Unable to load monsters!");
242+ return;
243+ }
244+
245+ std::cout << ">> Loading lua monsters" << std::endl;
246+ if (!g_scripts->loadScripts("monster", false, false)) {
247+ startupErrorMessage("Failed to load lua monsters");
248+ return;
249+ }
250+
251+ std::cout << ">> Loading outfits" << std::endl;
252+ if (!Outfits::getInstance().loadFromXml()) {
253+ startupErrorMessage("Unable to load outfits!");
254+ return;
255+ }
256+
257+ std::cout << ">> Checking world type... " << std::flush;
258+ std::string worldType = asLowerCaseString(g_config.getString(ConfigManager::WORLD_TYPE));
259+ if (worldType == "pvp") {
260+ g_game.setWorldType(WORLD_TYPE_PVP);
261+ } else if (worldType == "no-pvp") {
262+ g_game.setWorldType(WORLD_TYPE_NO_PVP);
263+ } else if (worldType == "pvp-enforced") {
264+ g_game.setWorldType(WORLD_TYPE_PVP_ENFORCED);
265+ } else {
266+ std::cout << std::endl;
267+
268+ std::ostringstream ss;
269+ ss << "> ERROR: Unknown world type: " << g_config.getString(ConfigManager::WORLD_TYPE) << ", valid world types are: pvp, no-pvp and pvp-enforced.";
270+ startupErrorMessage(ss.str());
271+ return;
272+ }
273+ std::cout << asUpperCaseString(worldType) << std::endl;
274+
275+ std::cout << ">> Loading map" << std::endl;
276+ if (!g_game.loadMainMap(g_config.getString(ConfigManager::MAP_NAME))) {
277+ startupErrorMessage("Failed to load map");
278+ return;
279+ }
280+
281+ std::cout << ">> Initializing gamestate" << std::endl;
282+ g_game.setGameState(GAME_STATE_INIT);
283+
284+ // Game client protocols
285+ services->add<ProtocolGame>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::GAME_PORT)));
286+ services->add<ProtocolLogin>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::LOGIN_PORT)));
287+
288+ // OT protocols
289+ services->add<ProtocolStatus>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::STATUS_PORT)));
290+
291+ // Legacy login protocol
292+ services->add<ProtocolOld>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::LOGIN_PORT)));
293+
294+ RentPeriod_t rentPeriod;
295+ std::string strRentPeriod = asLowerCaseString(g_config.getString(ConfigManager::HOUSE_RENT_PERIOD));
296+
297+ if (strRentPeriod == "yearly") {
298+ rentPeriod = RENTPERIOD_YEARLY;
299+ } else if (strRentPeriod == "weekly") {
300+ rentPeriod = RENTPERIOD_WEEKLY;
301+ } else if (strRentPeriod == "monthly") {
302+ rentPeriod = RENTPERIOD_MONTHLY;
303+ } else if (strRentPeriod == "daily") {
304+ rentPeriod = RENTPERIOD_DAILY;
305+ } else {
306+ rentPeriod = RENTPERIOD_NEVER;
307+ }
308+
309+ g_game.map.houses.payHouses(rentPeriod);
310+
311+ IOMarket::checkExpiredOffers();
312+ IOMarket::getInstance().updateStatistics();
313+
314+ std::cout << ">> Loaded all modules, server starting up..." << std::endl;
315+
316+#ifndef _WIN32
317+ if (getuid() == 0 || geteuid() == 0) {
318+ std::cout << "> Warning: " << STATUS_SERVER_NAME << " has been executed as root user, please consider running it as a normal user." << std::endl;
319+ }
320+#endif
321+
322+ g_game.start(services);
323+ g_game.setGameState(GAME_STATE_NORMAL);
324+ g_loaderSignal.notify_all();
325+}
A src/outfit.cpp +119-0 View file
@@ -0,0 +1,119 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#include "otpch.h"
21+
22+#include "outfit.h"
23+
24+#include "pugicast.h"
25+#include "tools.h"
26+
27+bool Outfits::loadFromXml() {
28+ pugi::xml_document doc;
29+ pugi::xml_parse_result result = doc.load_file("data/XML/outfits.xml");
30+ if (!result) {
31+ printXMLError("Error - Outfits::loadFromXml", "data/XML/outfits.xml",
32+ result);
33+ return false;
34+ }
35+
36+ for (auto outfitNode : doc.child("outfits").children()) {
37+ pugi::xml_attribute attr;
38+ if ((attr = outfitNode.attribute("enabled")) && !attr.as_bool()) {
39+ continue;
40+ }
41+
42+ if (!(attr = outfitNode.attribute("type"))) {
43+ std::cout << "[Warning - Outfits::loadFromXml] Missing outfit type."
44+ << std::endl;
45+ continue;
46+ }
47+
48+ uint16_t type = pugi::cast<uint16_t>(attr.value());
49+ if (type > PLAYERSEX_LAST) {
50+ std::cout << "[Warning - Outfits::loadFromXml] Invalid outfit type "
51+ << type << "." << std::endl;
52+ continue;
53+ }
54+
55+ pugi::xml_attribute lookTypeAttribute = outfitNode.attribute("looktype");
56+ if (!lookTypeAttribute) {
57+ std::cout
58+ << "[Warning - Outfits::loadFromXml] Missing looktype on outfit."
59+ << std::endl;
60+ continue;
61+ }
62+
63+ uint16_t attackMeleeType = 0;
64+ if ((attr = outfitNode.attribute("attackmeleetype"))) {
65+ attackMeleeType = pugi::cast<uint16_t>(attr.value());
66+ } else if ((attr = outfitNode.attribute("attacktype"))) {
67+ attackMeleeType = pugi::cast<uint16_t>(attr.value());
68+ }
69+
70+ uint16_t attackRangeType = 0;
71+ if ((attr = outfitNode.attribute("attackrangetype"))) {
72+ attackRangeType = pugi::cast<uint16_t>(attr.value());
73+ } else {
74+ attackRangeType = attackMeleeType;
75+ }
76+
77+ uint8_t attackRange = 1;
78+ if ((attr = outfitNode.attribute("attackrange"))) {
79+ attackRange = std::max<uint8_t>(
80+ 1, static_cast<uint8_t>(pugi::cast<uint16_t>(attr.value())));
81+ }
82+
83+ Outfit outfit(outfitNode.attribute("name").as_string(),
84+ pugi::cast<uint16_t>(lookTypeAttribute.value()),
85+ attackMeleeType, attackRangeType, attackRange,
86+ outfitNode.attribute("premium").as_bool(),
87+ outfitNode.attribute("unlocked").as_bool(true));
88+
89+ bool hasOffsets = false;
90+ for (auto offsetNode : outfitNode.children("offset")) {
91+ int dir = offsetNode.attribute("dir").as_int(-1);
92+ if (dir < 0 || dir >= OUTFIT_DIRECTIONS) {
93+ std::cout << "[Warning - Outfits::loadFromXml] Invalid offset dir "
94+ << dir << " on outfit " << outfit.name << "." << std::endl;
95+ continue;
96+ }
97+ hasOffsets = true;
98+ outfit.offsets[dir].lookX = offsetNode.attribute("lookX").as_int(0);
99+ outfit.offsets[dir].lookY = offsetNode.attribute("lookY").as_int(0);
100+ outfit.offsets[dir].meleeX = offsetNode.attribute("meleeX").as_int(0);
101+ outfit.offsets[dir].meleeY = offsetNode.attribute("meleeY").as_int(0);
102+ outfit.offsets[dir].rangeX = offsetNode.attribute("rangeX").as_int(0);
103+ outfit.offsets[dir].rangeY = offsetNode.attribute("rangeY").as_int(0);
104+ }
105+
106+ outfits[type].push_back(std::move(outfit));
107+ }
108+ return true;
109+}
110+
111+const Outfit *Outfits::getOutfitByLookType(PlayerSex_t sex,
112+ uint16_t lookType) const {
113+ for (const Outfit &outfit : outfits[sex]) {
114+ if (outfit.lookType == lookType) {
115+ return &outfit;
116+ }
117+ }
118+ return nullptr;
119+}
A src/outfit.h +86-0 View file
@@ -0,0 +1,86 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#ifndef FS_OUTFIT_H_C56E7A707E3F422C8C93D9BE09916AA3
21+#define FS_OUTFIT_H_C56E7A707E3F422C8C93D9BE09916AA3
22+
23+#include <string>
24+#include <vector>
25+
26+#include "enums.h"
27+
28+static constexpr int OUTFIT_DIRECTIONS = 4;
29+
30+struct Outfit {
31+ Outfit(std::string name, uint16_t lookType, uint16_t attackMeleeType,
32+ uint16_t attackRangeType, uint8_t attackRange, bool premium,
33+ bool unlocked)
34+ : name(std::move(name)), lookType(lookType),
35+ attackMeleeType(attackMeleeType), attackRangeType(attackRangeType),
36+ attackRange(attackRange), premium(premium), unlocked(unlocked) {
37+ for (int i = 0; i < OUTFIT_DIRECTIONS; ++i) {
38+ offsets[i] = OutfitOffset{};
39+ }
40+ }
41+
42+ std::string name;
43+ uint16_t lookType;
44+ uint16_t attackMeleeType;
45+ uint16_t attackRangeType;
46+ uint8_t attackRange;
47+ bool premium;
48+ bool unlocked;
49+ OutfitOffset offsets[OUTFIT_DIRECTIONS];
50+};
51+
52+struct ProtocolOutfit {
53+ ProtocolOutfit(const std::string &name, uint16_t lookType, uint8_t addons)
54+ : name(name), lookType(lookType), addons(addons) {}
55+
56+ const std::string &name;
57+ uint16_t lookType;
58+ uint8_t addons;
59+};
60+
61+class Outfits {
62+public:
63+ static Outfits &getInstance() {
64+ static Outfits instance;
65+ return instance;
66+ }
67+
68+ bool loadFromXml();
69+
70+ bool reload() {
71+ for (int i = 0; i <= PLAYERSEX_LAST; ++i) {
72+ outfits[i].clear();
73+ }
74+ return loadFromXml();
75+ }
76+
77+ const Outfit *getOutfitByLookType(PlayerSex_t sex, uint16_t lookType) const;
78+ const std::vector<Outfit> &getOutfits(PlayerSex_t sex) const {
79+ return outfits[sex];
80+ }
81+
82+private:
83+ std::vector<Outfit> outfits[PLAYERSEX_LAST + 1];
84+};
85+
86+#endif
A src/outputmessage.cpp +77-0 View file
@@ -0,0 +1,77 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#include "otpch.h"
21+
22+#include "outputmessage.h"
23+#include "protocol.h"
24+#include "lockfree.h"
25+#include "scheduler.h"
26+
27+extern Scheduler g_scheduler;
28+
29+const uint16_t OUTPUTMESSAGE_FREE_LIST_CAPACITY = 2048;
30+const std::chrono::milliseconds OUTPUTMESSAGE_AUTOSEND_DELAY {10};
31+
32+void OutputMessagePool::scheduleSendAll()
33+{
34+ auto functor = std::bind(&OutputMessagePool::sendAll, this);
35+ g_scheduler.addEvent(createSchedulerTask(OUTPUTMESSAGE_AUTOSEND_DELAY.count(), functor));
36+}
37+
38+void OutputMessagePool::sendAll()
39+{
40+ //dispatcher thread
41+ for (auto& protocol : bufferedProtocols) {
42+ auto& msg = protocol->getCurrentBuffer();
43+ if (msg) {
44+ protocol->send(std::move(msg));
45+ }
46+ }
47+
48+ if (!bufferedProtocols.empty()) {
49+ scheduleSendAll();
50+ }
51+}
52+
53+void OutputMessagePool::addProtocolToAutosend(Protocol_ptr protocol)
54+{
55+ //dispatcher thread
56+ if (bufferedProtocols.empty()) {
57+ scheduleSendAll();
58+ }
59+ bufferedProtocols.emplace_back(protocol);
60+}
61+
62+void OutputMessagePool::removeProtocolFromAutosend(const Protocol_ptr& protocol)
63+{
64+ //dispatcher thread
65+ auto it = std::find(bufferedProtocols.begin(), bufferedProtocols.end(), protocol);
66+ if (it != bufferedProtocols.end()) {
67+ std::swap(*it, bufferedProtocols.back());
68+ bufferedProtocols.pop_back();
69+ }
70+}
71+
72+OutputMessage_ptr OutputMessagePool::getOutputMessage()
73+{
74+ // LockfreePoolingAllocator<void,...> will leave (void* allocate) ill-formed because
75+ // of sizeof(T), so this guaranatees that only one list will be initialized
76+ return std::allocate_shared<OutputMessage>(LockfreePoolingAllocator<void, OUTPUTMESSAGE_FREE_LIST_CAPACITY>());
77+}
A src/outputmessage.h +110-0 View file
@@ -0,0 +1,110 @@
1+/**
2+ * The Forgotten Server - a free and open-source MMORPG server emulator
3+ * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
4+ *
5+ * This program is free software; you can redistribute it and/or modify
6+ * it under the terms of the GNU General Public License as published by
7+ * the Free Software Foundation; either version 2 of the License, or
8+ * (at your option) any later version.
9+ *
10+ * This program is distributed in the hope that it will be useful,
11+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+ * GNU General Public License for more details.
14+ *
15+ * You should have received a copy of the GNU General Public License along
16+ * with this program; if not, write to the Free Software Foundation, Inc.,
17+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18+ */
19+
20+#ifndef FS_OUTPUTMESSAGE_H_C06AAED85C7A43939F22D229297C0CC1
21+#define FS_OUTPUTMESSAGE_H_C06AAED85C7A43939F22D229297C0CC1
22+
23+#include "networkmessage.h"
24+#include "connection.h"
25+#include "tools.h"
26+
27+class Protocol;
28+
29+class OutputMessage : public NetworkMessage
30+{
31+ public:
32+ OutputMessage() = default;
33+
34+ // non-copyable
35+ OutputMessage(const OutputMessage&) = delete;
36+ OutputMessage& operator=(const OutputMessage&) = delete;
37+
38+ uint8_t* getOutputBuffer() {
39+ return buffer + outputBufferStart;
40+ }
41+
42+ void writeMessageLength() {
43+ add_header(info.length);
44+ }
45+
46+ void addCryptoHeader(bool addChecksum, bool compression = false) {
47+ if (compression) {
48+ add_header<uint32_t>(0);
49+ } else if (addChecksum) {
50+ add_header(adlerChecksum(buffer + outputBufferStart, info.length));
51+ }
52+
53+ writeMessageLength();
54+ }
55+
56+ void append(const NetworkMessage& msg) {
57+ auto msgLen = msg.getLength();
58+ memcpy(buffer + info.position, msg.getBuffer() + 8, msgLen);
59+ info.length += msgLen;
60+ info.position += msgLen;
61+ }
62+
63+ void append(const OutputMessage_ptr& msg) {
64+ auto msgLen = msg->getLength();
65+ memcpy(buffer + info.position, msg->getBuffer() + 8, msgLen);
66+ info.length += msgLen;
67+ info.position += msgLen;
68+ }
69+
70+ private:
71+ template <typename T>
72+ void add_header(T add) {
73+ assert(outputBufferStart >= sizeof(T));
74+ outputBufferStart -= sizeof(T);
75+ memcpy(buffer + outputBufferStart, &add, sizeof(T));
76+ //added header size to the message size
77+ info.length += sizeof(T);
78+ }
79+
80+ MsgSize_t outputBufferStart = INITIAL_BUFFER_POSITION;
81+};
82+
83+class OutputMessagePool
84+{
85+ public:
86+ // non-copyable
87+ OutputMessagePool(const OutputMessagePool&) = delete;
88+ OutputMessagePool& operator=(const OutputMessagePool&) = delete;
89+
90+ static OutputMessagePool& getInstance() {
91+ static OutputMessagePool instance;
92+ return instance;
93+ }
94+
95+ void sendAll();
96+ void scheduleSendAll();
97+
98+ static OutputMessage_ptr getOutputMessage();
99+
100+ void addProtocolToAutosend(Protocol_ptr protocol);
101+ void removeProtocolFromAutosend(const Protocol_ptr& protocol);
102+ private:
103+ OutputMessagePool() = default;
104+ //NOTE: A vector is used here because this container is mostly read
105+ //and relatively rarely modified (only when a client connects/disconnects)
106+ std::vector<Protocol_ptr> bufferedProtocols;
107+};
108+
109+
110+#endif
Top