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/position.cpp +73-0 View file
@@ -0,0 +1,73 @@
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 "position.h"
23+
24+std::ostream& operator<<(std::ostream& os, const Position& pos)
25+{
26+ os << "( " << std::setw(5) << std::setfill('0') << pos.x;
27+ os << " / " << std::setw(5) << std::setfill('0') << pos.y;
28+ os << " / " << std::setw(3) << std::setfill('0') << pos.getZ();
29+ os << " )";
30+ return os;
31+}
32+
33+std::ostream& operator<<(std::ostream& os, const Direction& dir)
34+{
35+ switch (dir) {
36+ case DIRECTION_NORTH:
37+ os << "North";
38+ break;
39+
40+ case DIRECTION_EAST:
41+ os << "East";
42+ break;
43+
44+ case DIRECTION_WEST:
45+ os << "West";
46+ break;
47+
48+ case DIRECTION_SOUTH:
49+ os << "South";
50+ break;
51+
52+ case DIRECTION_SOUTHWEST:
53+ os << "South-West";
54+ break;
55+
56+ case DIRECTION_SOUTHEAST:
57+ os << "South-East";
58+ break;
59+
60+ case DIRECTION_NORTHWEST:
61+ os << "North-West";
62+ break;
63+
64+ case DIRECTION_NORTHEAST:
65+ os << "North-East";
66+ break;
67+
68+ default:
69+ break;
70+ }
71+
72+ return os;
73+}
A src/position.h +134-0 View file
@@ -0,0 +1,134 @@
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_POSITION_H_5B684192F7034FB8857C8280D2CC6C75
21+#define FS_POSITION_H_5B684192F7034FB8857C8280D2CC6C75
22+
23+enum Direction : uint8_t {
24+ DIRECTION_NORTH = 0,
25+ DIRECTION_EAST = 1,
26+ DIRECTION_SOUTH = 2,
27+ DIRECTION_WEST = 3,
28+
29+ DIRECTION_DIAGONAL_MASK = 4,
30+ DIRECTION_NORTHEAST = DIRECTION_DIAGONAL_MASK | 0,
31+ DIRECTION_SOUTHEAST = DIRECTION_DIAGONAL_MASK | 1,
32+ DIRECTION_SOUTHWEST = DIRECTION_DIAGONAL_MASK | 2,
33+ DIRECTION_NORTHWEST = DIRECTION_DIAGONAL_MASK | 3,
34+
35+ DIRECTION_LAST = DIRECTION_NORTHWEST,
36+ DIRECTION_NONE = 8,
37+};
38+
39+struct Position {
40+ constexpr Position() = default;
41+ constexpr Position(uint16_t x, uint16_t y, uint8_t z) : x(x), y(y), z(z) {}
42+
43+ template <int_fast32_t deltax, int_fast32_t deltay>
44+ static bool areInRange(const Position &p1, const Position &p2) {
45+ return Position::getDistanceX(p1, p2) <= deltax &&
46+ Position::getDistanceY(p1, p2) <= deltay;
47+ }
48+
49+ template <int_fast32_t deltax, int_fast32_t deltay, int_fast16_t deltaz>
50+ static bool areInRange(const Position &p1, const Position &p2) {
51+ return Position::getDistanceX(p1, p2) <= deltax &&
52+ Position::getDistanceY(p1, p2) <= deltay &&
53+ Position::getDistanceZ(p1, p2) <= deltaz;
54+ }
55+
56+ static int_fast32_t getOffsetX(const Position &p1, const Position &p2) {
57+ return p1.getX() - p2.getX();
58+ }
59+ static int_fast32_t getOffsetY(const Position &p1, const Position &p2) {
60+ return p1.getY() - p2.getY();
61+ }
62+ static int_fast16_t getOffsetZ(const Position &p1, const Position &p2) {
63+ return p1.getZ() - p2.getZ();
64+ }
65+
66+ static int32_t getDistanceX(const Position &p1, const Position &p2) {
67+ return std::abs(Position::getOffsetX(p1, p2));
68+ }
69+ static int32_t getDistanceY(const Position &p1, const Position &p2) {
70+ return std::abs(Position::getOffsetY(p1, p2));
71+ }
72+ static int16_t getDistanceZ(const Position &p1, const Position &p2) {
73+ return std::abs(Position::getOffsetZ(p1, p2));
74+ }
75+
76+ uint16_t x = 0;
77+ uint16_t y = 0;
78+ uint8_t z = 0;
79+
80+ bool operator<(const Position &p) const {
81+ if (z < p.z) {
82+ return true;
83+ }
84+
85+ if (z > p.z) {
86+ return false;
87+ }
88+
89+ if (y < p.y) {
90+ return true;
91+ }
92+
93+ if (y > p.y) {
94+ return false;
95+ }
96+
97+ if (x < p.x) {
98+ return true;
99+ }
100+
101+ if (x > p.x) {
102+ return false;
103+ }
104+
105+ return false;
106+ }
107+
108+ bool operator>(const Position &p) const { return !(*this < p); }
109+
110+ bool operator==(const Position &p) const {
111+ return p.x == x && p.y == y && p.z == z;
112+ }
113+
114+ bool operator!=(const Position &p) const {
115+ return p.x != x || p.y != y || p.z != z;
116+ }
117+
118+ Position operator+(const Position &p1) const {
119+ return Position(x + p1.x, y + p1.y, z + p1.z);
120+ }
121+
122+ Position operator-(const Position &p1) const {
123+ return Position(x - p1.x, y - p1.y, z - p1.z);
124+ }
125+
126+ int_fast32_t getX() const { return x; }
127+ int_fast32_t getY() const { return y; }
128+ int_fast16_t getZ() const { return z; }
129+};
130+
131+std::ostream &operator<<(std::ostream &, const Position &);
132+std::ostream &operator<<(std::ostream &, const Direction &);
133+
134+#endif
A src/protocol.cpp +153-0 View file
@@ -0,0 +1,153 @@
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 "protocol.h"
23+#include "outputmessage.h"
24+#include "rsa.h"
25+#include "xtea.h"
26+
27+extern RSA g_RSA;
28+
29+Protocol::~Protocol()
30+{
31+ if (compression) {
32+ deflateEnd(&zstream);
33+ }
34+}
35+
36+void Protocol::onSendMessage(const OutputMessage_ptr& msg) const
37+{
38+ if (!rawMessages) {
39+ bool compressed = false;
40+ if (compression && msg->getLength() > 64) {
41+ compress(*msg);
42+ compressed = true;
43+ }
44+
45+ msg->writeMessageLength();
46+
47+ if (encryptionEnabled) {
48+ XTEA_encrypt(*msg);
49+ msg->addCryptoHeader(checksumEnabled, compressed);
50+ }
51+ }
52+}
53+
54+void Protocol::onRecvMessage(NetworkMessage& msg)
55+{
56+ if (encryptionEnabled && !XTEA_decrypt(msg)) {
57+ return;
58+ }
59+
60+ parsePacket(msg);
61+}
62+
63+OutputMessage_ptr Protocol::getOutputBuffer(int32_t size)
64+{
65+ //dispatcher thread
66+ if (!outputBuffer) {
67+ outputBuffer = OutputMessagePool::getOutputMessage();
68+ } else if ((outputBuffer->getLength() + size) > NetworkMessage::MAX_PROTOCOL_BODY_LENGTH) {
69+ send(outputBuffer);
70+ outputBuffer = OutputMessagePool::getOutputMessage();
71+ }
72+ return outputBuffer;
73+}
74+
75+void Protocol::XTEA_encrypt(OutputMessage& msg) const
76+{
77+ // The message must be a multiple of 8
78+ size_t paddingBytes = msg.getLength() % 8u;
79+ if (paddingBytes != 0) {
80+ msg.addPaddingBytes(8 - paddingBytes);
81+ }
82+
83+ uint8_t* buffer = msg.getOutputBuffer();
84+ xtea::encrypt(buffer, msg.getLength(), key);
85+}
86+
87+bool Protocol::XTEA_decrypt(NetworkMessage& msg) const
88+{
89+ if (((msg.getLength() - 6) & 7) != 0) {
90+ return false;
91+ }
92+
93+ uint8_t* buffer = msg.getBuffer() + msg.getBufferPosition();
94+ xtea::decrypt(buffer, msg.getLength() - 6, key);
95+
96+ uint16_t innerLength = msg.get<uint16_t>();
97+ if (innerLength + 8 > msg.getLength()) {
98+ return false;
99+ }
100+
101+ msg.setLength(innerLength);
102+ return true;
103+}
104+
105+bool Protocol::RSA_decrypt(NetworkMessage& msg)
106+{
107+ if ((msg.getLength() - msg.getBufferPosition()) < 128) {
108+ return false;
109+ }
110+
111+ g_RSA.decrypt(reinterpret_cast<char*>(msg.getBuffer()) + msg.getBufferPosition()); //does not break strict aliasing
112+ return msg.getByte() == 0;
113+}
114+
115+uint32_t Protocol::getIP() const
116+{
117+ if (auto connection = getConnection()) {
118+ return connection->getIP();
119+ }
120+
121+ return 0;
122+}
123+
124+void Protocol::enableCompression()
125+{
126+ if (compression)
127+ return;
128+ if (deflateInit2(&zstream, 6, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) {
129+ std::cerr << "ZLIB initialization error: " << (zstream.msg ? zstream.msg : "unknown") << std::endl;
130+ }
131+ compression = true;
132+}
133+
134+void Protocol::compress(OutputMessage& msg) const
135+{
136+ static thread_local std::vector<uint8_t> buffer(NETWORKMESSAGE_MAXSIZE);
137+ zstream.next_in = msg.getOutputBuffer();
138+ zstream.avail_in = msg.getLength();
139+ zstream.next_out = buffer.data();
140+ zstream.avail_out = buffer.size();
141+ if (deflate(&zstream, Z_SYNC_FLUSH) != Z_OK) {
142+ std::cerr << "ZLIB deflate error: " << (zstream.msg ? zstream.msg : "unknown") << std::endl;
143+ return;
144+ }
145+ int finalSize = buffer.size() - zstream.avail_out - 4;
146+ if (finalSize < 0) {
147+ std::cerr << "Packet compression error: " << (zstream.msg ? zstream.msg : "unknown") << std::endl;
148+ return;
149+ }
150+
151+ msg.reset();
152+ msg.addBytes((const char*)buffer.data(), finalSize);
153+}
A src/protocol.h +111-0 View file
@@ -0,0 +1,111 @@
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_PROTOCOL_H_D71405071ACF4137A4B1203899DE80E1
21+#define FS_PROTOCOL_H_D71405071ACF4137A4B1203899DE80E1
22+
23+#include <zlib.h>
24+
25+#include "connection.h"
26+#include "xtea.h"
27+
28+class Protocol : public std::enable_shared_from_this<Protocol>
29+{
30+ public:
31+ explicit Protocol(Connection_ptr connection) : connection(connection) {}
32+ virtual ~Protocol();
33+
34+ // non-copyable
35+ Protocol(const Protocol&) = delete;
36+ Protocol& operator=(const Protocol&) = delete;
37+
38+ virtual void parsePacket(NetworkMessage&) {}
39+
40+ virtual void onSendMessage(const OutputMessage_ptr& msg) const;
41+ void onRecvMessage(NetworkMessage& msg);
42+ virtual void onRecvFirstMessage(NetworkMessage& msg) = 0;
43+ virtual void onConnect() {}
44+
45+ bool isConnectionExpired() const {
46+ return connection.expired();
47+ }
48+
49+ Connection_ptr getConnection() const {
50+ return connection.lock();
51+ }
52+
53+ uint32_t getIP() const;
54+
55+ //Use this function for autosend messages only
56+ OutputMessage_ptr getOutputBuffer(int32_t size);
57+
58+ OutputMessage_ptr& getCurrentBuffer() {
59+ return outputBuffer;
60+ }
61+
62+ void send(OutputMessage_ptr msg) const {
63+ if (auto connection = getConnection()) {
64+ connection->send(msg);
65+ }
66+ }
67+
68+ protected:
69+ void disconnect() const {
70+ if (auto connection = getConnection()) {
71+ connection->close();
72+ }
73+ }
74+ void enableXTEAEncryption() {
75+ encryptionEnabled = true;
76+ }
77+ void setXTEAKey(xtea::key key) {
78+ this->key = std::move(key);
79+ }
80+ void disableChecksum() {
81+ checksumEnabled = false;
82+ }
83+
84+ static bool RSA_decrypt(NetworkMessage& msg);
85+
86+ void setRawMessages(bool value) {
87+ rawMessages = value;
88+ }
89+ void enableCompression();
90+
91+ virtual void release() {}
92+
93+ private:
94+ void XTEA_encrypt(OutputMessage& msg) const;
95+ bool XTEA_decrypt(NetworkMessage& msg) const;
96+ void compress(OutputMessage& msg) const;
97+
98+ friend class Connection;
99+
100+ OutputMessage_ptr outputBuffer;
101+
102+ const ConnectionWeak_ptr connection;
103+ xtea::key key;
104+ bool encryptionEnabled = false;
105+ bool checksumEnabled = true;
106+ bool rawMessages = false;
107+ bool compression = false;
108+ mutable z_stream zstream = {0};
109+};
110+
111+#endif
Top