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/spells.h +457-0 View file
@@ -0,0 +1,457 @@
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_SPELLS_H_D78A7CCB7080406E8CAA6B1D31D3DA71
21+#define FS_SPELLS_H_D78A7CCB7080406E8CAA6B1D31D3DA71
22+
23+#include "luascript.h"
24+#include "player.h"
25+#include "actions.h"
26+#include "talkaction.h"
27+#include "baseevents.h"
28+
29+class InstantSpell;
30+class RuneSpell;
31+class Spell;
32+
33+using VocSpellMap = std::map<uint16_t, bool>;
34+using InstantSpell_ptr = std::unique_ptr<InstantSpell>;
35+using RuneSpell_ptr = std::unique_ptr<RuneSpell>;
36+
37+class Spells final : public BaseEvents
38+{
39+ public:
40+ Spells();
41+ ~Spells();
42+
43+ // non-copyable
44+ Spells(const Spells&) = delete;
45+ Spells& operator=(const Spells&) = delete;
46+
47+ Spell* getSpellByName(const std::string& name);
48+ RuneSpell* getRuneSpell(uint32_t id);
49+ RuneSpell* getRuneSpellByName(const std::string& name);
50+
51+ InstantSpell* getInstantSpell(const std::string& words);
52+ InstantSpell* getInstantSpellByName(const std::string& name);
53+
54+ InstantSpell* getInstantSpellById(uint32_t spellId);
55+
56+ TalkActionResult_t playerSaySpell(Player* player, std::string& words);
57+
58+ static Position getCasterPosition(Creature* creature, Direction dir);
59+ std::string getScriptBaseName() const override;
60+
61+ const std::map<std::string, InstantSpell>& getInstantSpells() const {
62+ return instants;
63+ };
64+
65+ void clearMaps(bool fromLua);
66+ void clear(bool fromLua) override final;
67+ bool registerInstantLuaEvent(InstantSpell* event);
68+ bool registerRuneLuaEvent(RuneSpell* event);
69+
70+ private:
71+ LuaScriptInterface& getScriptInterface() override;
72+ Event_ptr getEvent(const std::string& nodeName) override;
73+ bool registerEvent(Event_ptr event, const pugi::xml_node& node) override;
74+
75+ std::map<uint16_t, RuneSpell> runes;
76+ std::map<std::string, InstantSpell> instants;
77+
78+ friend class CombatSpell;
79+ LuaScriptInterface scriptInterface { "Spell Interface" };
80+};
81+
82+using RuneSpellFunction = std::function<bool(const RuneSpell* spell, Player* player, const Position& posTo)>;
83+
84+class BaseSpell
85+{
86+ public:
87+ constexpr BaseSpell() = default;
88+ virtual ~BaseSpell() = default;
89+
90+ virtual bool castSpell(Creature* creature) = 0;
91+ virtual bool castSpell(Creature* creature, Creature* target) = 0;
92+};
93+
94+class CombatSpell final : public Event, public BaseSpell
95+{
96+ public:
97+ CombatSpell(Combat* combat, bool needTarget, bool needDirection);
98+ ~CombatSpell();
99+
100+ // non-copyable
101+ CombatSpell(const CombatSpell&) = delete;
102+ CombatSpell& operator=(const CombatSpell&) = delete;
103+
104+ bool castSpell(Creature* creature) override;
105+ bool castSpell(Creature* creature, Creature* target) override;
106+ bool configureEvent(const pugi::xml_node&) override {
107+ return true;
108+ }
109+
110+ //scripting
111+ bool executeCastSpell(Creature* creature, const LuaVariant& var);
112+
113+ bool loadScriptCombat();
114+ Combat* getCombat() {
115+ return combat;
116+ }
117+
118+ private:
119+ std::string getScriptEventName() const override {
120+ return "onCastSpell";
121+ }
122+
123+ Combat* combat;
124+
125+ bool needDirection;
126+ bool needTarget;
127+};
128+
129+class Spell : public BaseSpell
130+{
131+ public:
132+ Spell() = default;
133+
134+ bool configureSpell(const pugi::xml_node& node);
135+ const std::string& getName() const {
136+ return name;
137+ }
138+ void setName(std::string n) {
139+ name = n;
140+ }
141+ uint8_t getId() const {
142+ return spellId;
143+ }
144+ void setId(uint8_t id) {
145+ spellId = id;
146+ }
147+
148+ void postCastSpell(Player* player, bool finishedCast = true, bool payCost = true) const;
149+ static void postCastSpell(Player* player, uint32_t manaCost, uint32_t soulCost);
150+
151+ uint32_t getManaCost(const Player* player) const;
152+ uint32_t getSoulCost() const {
153+ return soul;
154+ }
155+ void setSoulCost(uint32_t s) {
156+ soul = s;
157+ }
158+ uint32_t getLevel() const {
159+ return level;
160+ }
161+ void setLevel(uint32_t lvl) {
162+ level = lvl;
163+ }
164+ uint32_t getMagicLevel() const {
165+ return magLevel;
166+ }
167+ void setMagicLevel(uint32_t lvl) {
168+ magLevel = lvl;
169+ }
170+ uint32_t getMana() const {
171+ return mana;
172+ }
173+ void setMana(uint32_t m) {
174+ mana = m;
175+ }
176+ uint32_t getManaPercent() const {
177+ return manaPercent;
178+ }
179+ void setManaPercent(uint32_t m) {
180+ manaPercent = m;
181+ }
182+ bool isPremium() const {
183+ return premium;
184+ }
185+ void setPremium(bool p) {
186+ premium = p;
187+ }
188+ bool isEnabled() const {
189+ return enabled;
190+ }
191+ void setEnabled(bool e) {
192+ enabled = e;
193+ }
194+
195+ virtual bool isInstant() const = 0;
196+ bool isLearnable() const {
197+ return learnable;
198+ }
199+ void setLearnable(bool l) {
200+ learnable = l;
201+ }
202+
203+ const VocSpellMap& getVocMap() const {
204+ return vocSpellMap;
205+ }
206+ void addVocMap(uint16_t n, bool b) {
207+ vocSpellMap[n] = b;
208+ }
209+
210+ const SpellGroup_t getGroup() const {
211+ return group;
212+ }
213+ void setGroup(SpellGroup_t g) {
214+ group = g;
215+ }
216+ const SpellGroup_t getSecondaryGroup() const {
217+ return secondaryGroup;
218+ }
219+ void setSecondaryGroup(SpellGroup_t g) {
220+ secondaryGroup = g;
221+ }
222+
223+ uint32_t getCooldown() const {
224+ return cooldown;
225+ }
226+ void setCooldown(uint32_t cd) {
227+ cooldown = cd;
228+ }
229+ uint32_t getSecondaryCooldown() const {
230+ return secondaryGroupCooldown;
231+ }
232+ void setSecondaryCooldown(uint32_t cd) {
233+ secondaryGroupCooldown = cd;
234+ }
235+ uint32_t getGroupCooldown() const {
236+ return groupCooldown;
237+ }
238+ void setGroupCooldown(uint32_t cd) {
239+ groupCooldown = cd;
240+ }
241+
242+ int32_t getRange() const {
243+ return range;
244+ }
245+ void setRange(int32_t r) {
246+ range = r;
247+ }
248+
249+ bool getNeedTarget() const {
250+ return needTarget;
251+ }
252+ void setNeedTarget(bool n) {
253+ needTarget = n;
254+ }
255+ bool getNeedWeapon() const {
256+ return needWeapon;
257+ }
258+ void setNeedWeapon(bool n) {
259+ needWeapon = n;
260+ }
261+ bool getNeedLearn() const {
262+ return learnable;
263+ }
264+ void setNeedLearn(bool n) {
265+ learnable = n;
266+ }
267+ bool getSelfTarget() const {
268+ return selfTarget;
269+ }
270+ void setSelfTarget(bool s) {
271+ selfTarget = s;
272+ }
273+ bool getBlockingSolid() const {
274+ return blockingSolid;
275+ }
276+ void setBlockingSolid(bool b) {
277+ blockingSolid = b;
278+ }
279+ bool getBlockingCreature() const {
280+ return blockingCreature;
281+ }
282+ void setBlockingCreature(bool b) {
283+ blockingCreature = b;
284+ }
285+ bool getAggressive() const {
286+ return aggressive;
287+ }
288+ void setAggressive(bool a) {
289+ aggressive = a;
290+ }
291+ bool getPzLock() const {
292+ return pzLock;
293+ }
294+ void setPzLock(bool pzLock) {
295+ this->pzLock = pzLock;
296+ }
297+
298+ SpellType_t spellType = SPELL_UNDEFINED;
299+
300+ protected:
301+ bool playerSpellCheck(Player* player) const;
302+ bool playerInstantSpellCheck(Player* player, const Position& toPos);
303+ bool playerRuneSpellCheck(Player* player, const Position& toPos);
304+
305+ VocSpellMap vocSpellMap;
306+
307+ SpellGroup_t group = SPELLGROUP_NONE;
308+ SpellGroup_t secondaryGroup = SPELLGROUP_NONE;
309+
310+ uint32_t cooldown = 1000;
311+ uint32_t groupCooldown = 1000;
312+ uint32_t secondaryGroupCooldown = 0;
313+ uint32_t level = 0;
314+ uint32_t magLevel = 0;
315+ int32_t range = -1;
316+
317+ uint8_t spellId = 0;
318+
319+ bool selfTarget = false;
320+ bool needTarget = false;
321+
322+ private:
323+
324+ uint32_t mana = 0;
325+ uint32_t manaPercent = 0;
326+ uint32_t soul = 0;
327+
328+ bool needWeapon = false;
329+ bool blockingSolid = false;
330+ bool blockingCreature = false;
331+ bool aggressive = true;
332+ bool pzLock = false;
333+ bool learnable = false;
334+ bool enabled = true;
335+ bool premium = false;
336+
337+
338+ private:
339+ std::string name;
340+};
341+
342+class InstantSpell final : public TalkAction, public Spell
343+{
344+ public:
345+ explicit InstantSpell(LuaScriptInterface* interface) : TalkAction(interface) {}
346+
347+ bool configureEvent(const pugi::xml_node& node) override;
348+
349+ virtual bool playerCastInstant(Player* player, std::string& param);
350+
351+ bool castSpell(Creature* creature) override;
352+ bool castSpell(Creature* creature, Creature* target) override;
353+
354+ //scripting
355+ bool executeCastSpell(Creature* creature, const LuaVariant& var);
356+
357+ bool isInstant() const override {
358+ return true;
359+ }
360+ bool getHasParam() const {
361+ return hasParam;
362+ }
363+ void setHasParam(bool p) {
364+ hasParam = p;
365+ }
366+ bool getHasPlayerNameParam() const {
367+ return hasPlayerNameParam;
368+ }
369+ void setHasPlayerNameParam(bool p) {
370+ hasPlayerNameParam = p;
371+ }
372+ bool getNeedDirection() const {
373+ return needDirection;
374+ }
375+ void setNeedDirection(bool n) {
376+ needDirection = n;
377+ }
378+ bool getNeedCasterTargetOrDirection() const {
379+ return casterTargetOrDirection;
380+ }
381+ void setNeedCasterTargetOrDirection(bool d) {
382+ casterTargetOrDirection = d;
383+ }
384+ bool getBlockWalls() const {
385+ return checkLineOfSight;
386+ }
387+ void setBlockWalls(bool w) {
388+ checkLineOfSight = w;
389+ }
390+ bool canCast(const Player* player) const;
391+ bool canThrowSpell(const Creature* creature, const Creature* target) const;
392+
393+ private:
394+ std::string getScriptEventName() const override;
395+
396+ bool internalCastSpell(Creature* creature, const LuaVariant& var);
397+
398+ bool needDirection = false;
399+ bool hasParam = false;
400+ bool hasPlayerNameParam = false;
401+ bool checkLineOfSight = true;
402+ bool casterTargetOrDirection = false;
403+};
404+
405+class RuneSpell final : public Action, public Spell
406+{
407+ public:
408+ explicit RuneSpell(LuaScriptInterface* interface) : Action(interface) {}
409+
410+ bool configureEvent(const pugi::xml_node& node) override;
411+
412+ ReturnValue canExecuteAction(const Player* player, const Position& toPos) override;
413+ bool hasOwnErrorHandler() override {
414+ return true;
415+ }
416+ Thing* getTarget(Player*, Creature* targetCreature, const Position&, uint8_t) const override {
417+ return targetCreature;
418+ }
419+
420+ bool executeUse(Player* player, Item* item, const Position& fromPosition, Thing* target, const Position& toPosition, bool isHotkey) override;
421+
422+ bool castSpell(Creature* creature) override;
423+ bool castSpell(Creature* creature, Creature* target) override;
424+
425+ //scripting
426+ bool executeCastSpell(Creature* creature, const LuaVariant& var, bool isHotkey);
427+
428+ bool isInstant() const override {
429+ return false;
430+ }
431+ uint16_t getRuneItemId() const {
432+ return runeId;
433+ }
434+ void setRuneItemId(uint16_t i) {
435+ runeId = i;
436+ }
437+ uint32_t getCharges() const {
438+ return charges;
439+ }
440+ void setCharges(uint32_t c) {
441+ if (c > 0) {
442+ hasCharges = true;
443+ }
444+ charges = c;
445+ }
446+
447+ private:
448+ std::string getScriptEventName() const override;
449+
450+ bool internalCastSpell(Creature* creature, const LuaVariant& var, bool isHotkey);
451+
452+ uint16_t runeId = 0;
453+ uint32_t charges = 0;
454+ bool hasCharges = false;
455+};
456+
457+#endif
A src/talkaction.cpp +169-0 View file
@@ -0,0 +1,169 @@
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 "player.h"
23+#include "talkaction.h"
24+#include "pugicast.h"
25+
26+TalkActions::TalkActions()
27+ : scriptInterface("TalkAction Interface")
28+{
29+ scriptInterface.initState();
30+}
31+
32+TalkActions::~TalkActions()
33+{
34+ clear(false);
35+}
36+
37+void TalkActions::clear(bool fromLua)
38+{
39+ for (auto it = talkActions.begin(); it != talkActions.end(); ) {
40+ if (fromLua == it->second.fromLua) {
41+ it = talkActions.erase(it);
42+ } else {
43+ ++it;
44+ }
45+ }
46+
47+ reInitState(fromLua);
48+}
49+
50+LuaScriptInterface& TalkActions::getScriptInterface()
51+{
52+ return scriptInterface;
53+}
54+
55+std::string TalkActions::getScriptBaseName() const
56+{
57+ return "talkactions";
58+}
59+
60+Event_ptr TalkActions::getEvent(const std::string& nodeName)
61+{
62+ if (strcasecmp(nodeName.c_str(), "talkaction") != 0) {
63+ return nullptr;
64+ }
65+ return Event_ptr(new TalkAction(&scriptInterface));
66+}
67+
68+bool TalkActions::registerEvent(Event_ptr event, const pugi::xml_node&)
69+{
70+ TalkAction_ptr talkAction{static_cast<TalkAction*>(event.release())}; // event is guaranteed to be a TalkAction
71+ talkActions.emplace(talkAction->getWords(), std::move(*talkAction));
72+ return true;
73+}
74+
75+bool TalkActions::registerLuaEvent(TalkAction* event)
76+{
77+ TalkAction_ptr talkAction{ event };
78+ talkActions.emplace(talkAction->getWords(), std::move(*talkAction));
79+ return true;
80+}
81+
82+TalkActionResult_t TalkActions::playerSaySpell(Player* player, SpeakClasses type, const std::string& words) const
83+{
84+ size_t wordsLength = words.length();
85+ for (auto it = talkActions.begin(); it != talkActions.end(); ) {
86+ const std::string& talkactionWords = it->first;
87+ size_t talkactionLength = talkactionWords.length();
88+ if (wordsLength < talkactionLength || strncasecmp(words.c_str(), talkactionWords.c_str(), talkactionLength) != 0) {
89+ ++it;
90+ continue;
91+ }
92+
93+ std::string param;
94+ if (wordsLength != talkactionLength) {
95+ param = words.substr(talkactionLength);
96+ if (param.front() != ' ') {
97+ ++it;
98+ continue;
99+ }
100+ trim_left(param, ' ');
101+
102+ std::string separator = it->second.getSeparator();
103+ if (separator != " ") {
104+ if (!param.empty()) {
105+ if (param != separator) {
106+ ++it;
107+ continue;
108+ } else {
109+ param.erase(param.begin());
110+ }
111+ }
112+ }
113+ }
114+
115+ if (it->second.executeSay(player, param, type)) {
116+ return TALKACTION_CONTINUE;
117+ } else {
118+ return TALKACTION_BREAK;
119+ }
120+ }
121+ return TALKACTION_CONTINUE;
122+}
123+
124+bool TalkAction::configureEvent(const pugi::xml_node& node)
125+{
126+ pugi::xml_attribute wordsAttribute = node.attribute("words");
127+ if (!wordsAttribute) {
128+ std::cout << "[Error - TalkAction::configureEvent] Missing words for talk action or spell" << std::endl;
129+ return false;
130+ }
131+
132+ pugi::xml_attribute separatorAttribute = node.attribute("separator");
133+ if (separatorAttribute) {
134+ separator = pugi::cast<char>(separatorAttribute.value());
135+ }
136+
137+ words = wordsAttribute.as_string();
138+ return true;
139+}
140+
141+std::string TalkAction::getScriptEventName() const
142+{
143+ return "onSay";
144+}
145+
146+bool TalkAction::executeSay(Player* player, const std::string& param, SpeakClasses type) const
147+{
148+ //onSay(player, words, param, type)
149+ if (!scriptInterface->reserveScriptEnv()) {
150+ std::cout << "[Error - TalkAction::executeSay] Call stack overflow" << std::endl;
151+ return false;
152+ }
153+
154+ ScriptEnvironment* env = scriptInterface->getScriptEnv();
155+ env->setScriptId(scriptId, scriptInterface);
156+
157+ lua_State* L = scriptInterface->getLuaState();
158+
159+ scriptInterface->pushFunction(scriptId);
160+
161+ LuaScriptInterface::pushUserdata<Player>(L, player);
162+ LuaScriptInterface::setMetatable(L, -1, "Player");
163+
164+ LuaScriptInterface::pushString(L, words);
165+ LuaScriptInterface::pushString(L, param);
166+ lua_pushnumber(L, type);
167+
168+ return scriptInterface->callFunction(4);
169+}
A src/talkaction.h +93-0 View file
@@ -0,0 +1,93 @@
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_TALKACTION_H_E6AABAC0F89843469526ADF310F3131C
21+#define FS_TALKACTION_H_E6AABAC0F89843469526ADF310F3131C
22+
23+#include "luascript.h"
24+#include "baseevents.h"
25+#include "const.h"
26+
27+class TalkAction;
28+using TalkAction_ptr = std::unique_ptr<TalkAction>;
29+
30+enum TalkActionResult_t {
31+ TALKACTION_CONTINUE,
32+ TALKACTION_BREAK,
33+ TALKACTION_FAILED,
34+};
35+
36+class TalkAction : public Event
37+{
38+ public:
39+ explicit TalkAction(LuaScriptInterface* interface) : Event(interface) {}
40+
41+ bool configureEvent(const pugi::xml_node& node) override;
42+
43+ const std::string& getWords() const {
44+ return words;
45+ }
46+ void setWords(std::string word) {
47+ words = word;
48+ }
49+ std::string getSeparator() const {
50+ return separator;
51+ }
52+ void setSeparator(std::string sep) {
53+ separator = sep;
54+ }
55+
56+ //scripting
57+ bool executeSay(Player* player, const std::string& param, SpeakClasses type) const;
58+ //
59+
60+ private:
61+ std::string getScriptEventName() const override;
62+
63+ std::string words;
64+ std::string separator = "\"";
65+};
66+
67+class TalkActions final : public BaseEvents
68+{
69+ public:
70+ TalkActions();
71+ ~TalkActions();
72+
73+ // non-copyable
74+ TalkActions(const TalkActions&) = delete;
75+ TalkActions& operator=(const TalkActions&) = delete;
76+
77+ TalkActionResult_t playerSaySpell(Player* player, SpeakClasses type, const std::string& words) const;
78+
79+ bool registerLuaEvent(TalkAction* event);
80+ void clear(bool fromLua) override final;
81+
82+ private:
83+ LuaScriptInterface& getScriptInterface() override;
84+ std::string getScriptBaseName() const override;
85+ Event_ptr getEvent(const std::string& nodeName) override;
86+ bool registerEvent(Event_ptr event, const pugi::xml_node& node) override;
87+
88+ std::map<std::string, TalkAction> talkActions;
89+
90+ LuaScriptInterface scriptInterface;
91+};
92+
93+#endif
A src/tasks.cpp +106-0 View file
@@ -0,0 +1,106 @@
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 "tasks.h"
23+#include "game.h"
24+
25+extern Game g_game;
26+
27+Task* createTask(std::function<void (void)> f)
28+{
29+ return new Task(std::move(f));
30+}
31+
32+Task* createTask(uint32_t expiration, std::function<void (void)> f)
33+{
34+ return new Task(expiration, std::move(f));
35+}
36+
37+void Dispatcher::threadMain()
38+{
39+ // NOTE: second argument defer_lock is to prevent from immediate locking
40+ std::unique_lock<std::mutex> taskLockUnique(taskLock, std::defer_lock);
41+
42+ while (getState() != THREAD_STATE_TERMINATED) {
43+ // check if there are tasks waiting
44+ taskLockUnique.lock();
45+
46+ if (taskList.empty()) {
47+ //if the list is empty wait for signal
48+ taskSignal.wait(taskLockUnique);
49+ }
50+
51+ if (!taskList.empty()) {
52+ // take the first task
53+ Task* task = taskList.front();
54+ taskList.pop_front();
55+ taskLockUnique.unlock();
56+
57+ if (!task->hasExpired()) {
58+ ++dispatcherCycle;
59+ // execute it
60+ (*task)();
61+ }
62+ delete task;
63+ } else {
64+ taskLockUnique.unlock();
65+ }
66+ }
67+}
68+
69+void Dispatcher::addTask(Task* task, bool push_front /*= false*/)
70+{
71+ bool do_signal = false;
72+
73+ taskLock.lock();
74+
75+ if (getState() == THREAD_STATE_RUNNING) {
76+ do_signal = taskList.empty();
77+
78+ if (push_front) {
79+ taskList.push_front(task);
80+ } else {
81+ taskList.push_back(task);
82+ }
83+ } else {
84+ delete task;
85+ }
86+
87+ taskLock.unlock();
88+
89+ // send a signal if the list was empty
90+ if (do_signal) {
91+ taskSignal.notify_one();
92+ }
93+}
94+
95+void Dispatcher::shutdown()
96+{
97+ Task* task = createTask([this]() {
98+ setState(THREAD_STATE_TERMINATED);
99+ taskSignal.notify_one();
100+ });
101+
102+ std::lock_guard<std::mutex> lockClass(taskLock);
103+ taskList.push_back(task);
104+
105+ taskSignal.notify_one();
106+}
A src/tasks.h +90-0 View file
@@ -0,0 +1,90 @@
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_TASKS_H_A66AC384766041E59DCA059DAB6E1976
21+#define FS_TASKS_H_A66AC384766041E59DCA059DAB6E1976
22+
23+#include <condition_variable>
24+#include "thread_holder_base.h"
25+#include "enums.h"
26+
27+const int DISPATCHER_TASK_EXPIRATION = 2000;
28+const auto SYSTEM_TIME_ZERO = std::chrono::system_clock::time_point(std::chrono::milliseconds(0));
29+
30+class Task
31+{
32+ public:
33+ // DO NOT allocate this class on the stack
34+ explicit Task(std::function<void (void)>&& f) : func(std::move(f)) {}
35+ Task(uint32_t ms, std::function<void (void)>&& f) :
36+ expiration(std::chrono::system_clock::now() + std::chrono::milliseconds(ms)), func(std::move(f)) {}
37+
38+ virtual ~Task() = default;
39+ void operator()() {
40+ func();
41+ }
42+
43+ void setDontExpire() {
44+ expiration = SYSTEM_TIME_ZERO;
45+ }
46+
47+ bool hasExpired() const {
48+ if (expiration == SYSTEM_TIME_ZERO) {
49+ return false;
50+ }
51+ return expiration < std::chrono::system_clock::now();
52+ }
53+
54+ protected:
55+ std::chrono::system_clock::time_point expiration = SYSTEM_TIME_ZERO;
56+
57+ private:
58+ // Expiration has another meaning for scheduler tasks,
59+ // then it is the time the task should be added to the
60+ // dispatcher
61+ std::function<void (void)> func;
62+};
63+
64+Task* createTask(std::function<void (void)> f);
65+Task* createTask(uint32_t expiration, std::function<void (void)> f);
66+
67+class Dispatcher : public ThreadHolder<Dispatcher> {
68+ public:
69+ void addTask(Task* task, bool push_front = false);
70+
71+ void shutdown();
72+
73+ uint64_t getDispatcherCycle() const {
74+ return dispatcherCycle;
75+ }
76+
77+ void threadMain();
78+
79+ private:
80+ std::thread thread;
81+ std::mutex taskLock;
82+ std::condition_variable taskSignal;
83+
84+ std::list<Task*> taskList;
85+ uint64_t dispatcherCycle = 0;
86+};
87+
88+extern Dispatcher g_dispatcher;
89+
90+#endif
A src/teleport.cpp +145-0 View file
@@ -0,0 +1,145 @@
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 "teleport.h"
23+#include "game.h"
24+
25+extern Game g_game;
26+
27+Attr_ReadValue Teleport::readAttr(AttrTypes_t attr, PropStream& propStream)
28+{
29+ if (attr == ATTR_TELE_DEST) {
30+ if (!propStream.read<uint16_t>(destPos.x) || !propStream.read<uint16_t>(destPos.y) || !propStream.read<uint8_t>(destPos.z)) {
31+ return ATTR_READ_ERROR;
32+ }
33+ return ATTR_READ_CONTINUE;
34+ }
35+ return Item::readAttr(attr, propStream);
36+}
37+
38+void Teleport::serializeAttr(PropWriteStream& propWriteStream) const
39+{
40+ Item::serializeAttr(propWriteStream);
41+
42+ propWriteStream.write<uint8_t>(ATTR_TELE_DEST);
43+ propWriteStream.write<uint16_t>(destPos.x);
44+ propWriteStream.write<uint16_t>(destPos.y);
45+ propWriteStream.write<uint8_t>(destPos.z);
46+}
47+
48+ReturnValue Teleport::queryAdd(int32_t, const Thing&, uint32_t, uint32_t, Creature*) const
49+{
50+ return RETURNVALUE_NOTPOSSIBLE;
51+}
52+
53+ReturnValue Teleport::queryMaxCount(int32_t, const Thing&, uint32_t, uint32_t&, uint32_t) const
54+{
55+ return RETURNVALUE_NOTPOSSIBLE;
56+}
57+
58+ReturnValue Teleport::queryRemove(const Thing&, uint32_t, uint32_t) const
59+{
60+ return RETURNVALUE_NOERROR;
61+}
62+
63+Cylinder* Teleport::queryDestination(int32_t&, const Thing&, Item**, uint32_t&)
64+{
65+ return this;
66+}
67+
68+void Teleport::addThing(Thing* thing)
69+{
70+ return addThing(0, thing);
71+}
72+
73+bool Teleport::checkInfinityLoop(Tile* destTile)
74+{
75+ if (!destTile) {
76+ return false;
77+ }
78+
79+ if (Teleport* teleport = destTile->getTeleportItem()) {
80+ const Position& nextDestPos = teleport->getDestPos();
81+ if (getPosition() == nextDestPos) {
82+ return true;
83+ }
84+ return checkInfinityLoop(g_game.map.getTile(nextDestPos));
85+ }
86+ return false;
87+}
88+
89+void Teleport::addThing(int32_t, Thing* thing)
90+{
91+ Tile* destTile = g_game.map.getTile(destPos);
92+ if (!destTile) {
93+ return;
94+ }
95+
96+ // Prevent infinity loop
97+ if (checkInfinityLoop(destTile)) {
98+ const Position& pos = getPosition();
99+ std::cout << "Warning: infinity loop teleport. " << pos << std::endl;
100+ return;
101+ }
102+
103+ const MagicEffectClasses effect = Item::items[id].magicEffect;
104+
105+ if (Creature* creature = thing->getCreature()) {
106+ Position origPos = creature->getPosition();
107+ g_game.internalCreatureTurn(creature, origPos.x > destPos.x ? DIRECTION_WEST : DIRECTION_EAST);
108+ g_game.map.moveCreature(*creature, *destTile);
109+ if (effect != CONST_ME_NONE) {
110+ g_game.addMagicEffect(origPos, effect);
111+ g_game.addMagicEffect(destTile->getPosition(), effect);
112+ }
113+ } else if (Item* item = thing->getItem()) {
114+ if (effect != CONST_ME_NONE) {
115+ g_game.addMagicEffect(destTile->getPosition(), effect);
116+ g_game.addMagicEffect(item->getPosition(), effect);
117+ }
118+ g_game.internalMoveItem(getTile(), destTile, INDEX_WHEREEVER, item, item->getItemCount(), nullptr, FLAG_NOLIMIT);
119+ }
120+}
121+
122+void Teleport::updateThing(Thing*, uint16_t, uint32_t)
123+{
124+ //
125+}
126+
127+void Teleport::replaceThing(uint32_t, Thing*)
128+{
129+ //
130+}
131+
132+void Teleport::removeThing(Thing*, uint32_t)
133+{
134+ //
135+}
136+
137+void Teleport::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t)
138+{
139+ getParent()->postAddNotification(thing, oldParent, index, LINK_PARENT);
140+}
141+
142+void Teleport::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t)
143+{
144+ getParent()->postRemoveNotification(thing, newParent, index, LINK_PARENT);
145+}
A src/teleport.h +74-0 View file
@@ -0,0 +1,74 @@
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_TELEPORT_H_873B7F7F1DB24101A7ACFB54B25E0ABC
21+#define FS_TELEPORT_H_873B7F7F1DB24101A7ACFB54B25E0ABC
22+
23+#include "tile.h"
24+
25+class Teleport final : public Item, public Cylinder
26+{
27+ public:
28+ explicit Teleport(uint16_t type) : Item(type) {};
29+
30+ Teleport* getTeleport() override {
31+ return this;
32+ }
33+ const Teleport* getTeleport() const override {
34+ return this;
35+ }
36+
37+ //serialization
38+ Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override;
39+ void serializeAttr(PropWriteStream& propWriteStream) const override;
40+
41+ const Position& getDestPos() const {
42+ return destPos;
43+ }
44+ void setDestPos(Position pos) {
45+ destPos = std::move(pos);
46+ }
47+
48+ bool checkInfinityLoop(Tile* destTile);
49+
50+ //cylinder implementations
51+ ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count,
52+ uint32_t flags, Creature* actor = nullptr) const override;
53+ ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count,
54+ uint32_t& maxQueryCount, uint32_t flags) const override;
55+ ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override;
56+ Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem,
57+ uint32_t& flags) override;
58+
59+ void addThing(Thing* thing) override;
60+ void addThing(int32_t index, Thing* thing) override;
61+
62+ void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override;
63+ void replaceThing(uint32_t index, Thing* thing) override;
64+
65+ void removeThing(Thing* thing, uint32_t count) override;
66+
67+ void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
68+ void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
69+
70+ private:
71+ Position destPos;
72+};
73+
74+#endif
A src/thing.cpp +42-0 View file
@@ -0,0 +1,42 @@
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 "thing.h"
23+#include "tile.h"
24+
25+const Position& Thing::getPosition() const
26+{
27+ const Tile* tile = getTile();
28+ if (!tile) {
29+ return Tile::nullptr_tile.getPosition();
30+ }
31+ return tile->getPosition();
32+}
33+
34+Tile* Thing::getTile()
35+{
36+ return dynamic_cast<Tile*>(this);
37+}
38+
39+const Tile* Thing::getTile() const
40+{
41+ return dynamic_cast<const Tile*>(this);
42+}
A src/thing.h +66-0 View file
@@ -0,0 +1,66 @@
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_THING_H_6F16A8E566AF4ACEAE02CF32A7246144
21+#define FS_THING_H_6F16A8E566AF4ACEAE02CF32A7246144
22+
23+#include "position.h"
24+
25+class Tile;
26+class Cylinder;
27+class Item;
28+class Creature;
29+class Container;
30+
31+class Thing {
32+public:
33+ constexpr Thing() = default;
34+ virtual ~Thing() = default;
35+
36+ // non-copyable
37+ Thing(const Thing &) = delete;
38+ Thing &operator=(const Thing &) = delete;
39+
40+ virtual std::string getDescription(int32_t lookDistance) const = 0;
41+
42+ virtual Cylinder *getParent() const { return nullptr; }
43+ virtual Cylinder *getRealParent() const { return getParent(); }
44+
45+ virtual void setParent(Cylinder *) {
46+ //
47+ }
48+
49+ virtual Tile *getTile();
50+ virtual const Tile *getTile() const;
51+
52+ virtual const Position &getPosition() const;
53+ virtual int32_t getThrowRange() const = 0;
54+ virtual bool isPushable() const = 0;
55+
56+ virtual Container *getContainer() { return nullptr; }
57+ virtual const Container *getContainer() const { return nullptr; }
58+ virtual Item *getItem() { return nullptr; }
59+ virtual const Item *getItem() const { return nullptr; }
60+ virtual Creature *getCreature() { return nullptr; }
61+ virtual const Creature *getCreature() const { return nullptr; }
62+
63+ virtual bool isRemoved() const { return true; }
64+};
65+
66+#endif
A src/thread_holder_base.h +59-0 View file
@@ -0,0 +1,59 @@
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_THREAD_HOLDER_H_BEB56FC46748E71D15A5BF0773ED2E67
21+#define FS_THREAD_HOLDER_H_BEB56FC46748E71D15A5BF0773ED2E67
22+
23+#include <thread>
24+#include <atomic>
25+#include "enums.h"
26+
27+template <typename Derived>
28+class ThreadHolder
29+{
30+ public:
31+ ThreadHolder() {}
32+ void start() {
33+ setState(THREAD_STATE_RUNNING);
34+ thread = std::thread(&Derived::threadMain, static_cast<Derived*>(this));
35+ }
36+
37+ void stop() {
38+ setState(THREAD_STATE_CLOSING);
39+ }
40+
41+ void join() {
42+ if (thread.joinable()) {
43+ thread.join();
44+ }
45+ }
46+ protected:
47+ void setState(ThreadState newState) {
48+ threadState.store(newState, std::memory_order_relaxed);
49+ }
50+
51+ ThreadState getState() const {
52+ return threadState.load(std::memory_order_relaxed);
53+ }
54+ private:
55+ std::atomic<ThreadState> threadState{THREAD_STATE_TERMINATED};
56+ std::thread thread;
57+};
58+
59+#endif
Top