Client Upload Part 2

RD Core / Commit #5

Commit Client Upload Part 2

Alex Alex committed 19/10/2026 00:06 main Patch
1,244 files +271,215 -0
A RD_Client/src/framework/luaengine/luavaluecasts.h +515-0 View file
@@ -0,0 +1,515 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef LUAVALUECASTS_H
24+#define LUAVALUECASTS_H
25+
26+// this file is and must be included only from luainterface.h
27+
28+#include "declarations.h"
29+#include <framework/otml/declarations.h>
30+
31+template<typename T>
32+int push_internal_luavalue(T v);
33+
34+// bool
35+int push_luavalue(bool b);
36+bool luavalue_cast(int index, bool& b);
37+
38+// int
39+int push_luavalue(int i);
40+bool luavalue_cast(int index, int& i);
41+
42+// double
43+int push_luavalue(double d);
44+bool luavalue_cast(int index, double& d);
45+
46+// float
47+inline int push_luavalue(float f) { push_luavalue((double)f); return 1; }
48+inline bool luavalue_cast(int index, float& f) { double d; bool r = luavalue_cast(index, d); f = d; return r; }
49+
50+// int8
51+inline int push_luavalue(int8 v) { push_luavalue((int)v); return 1; }
52+inline bool luavalue_cast(int index, int8& v) { int i; bool r = luavalue_cast(index, i); v = i; return r; }
53+// uint8
54+inline int push_luavalue(uint8 v) { push_luavalue((int)v); return 1; }
55+inline bool luavalue_cast(int index, uint8& v){ int i; bool r = luavalue_cast(index, i); v = i; return r; }
56+// int16
57+inline int push_luavalue(int16 v) { push_luavalue((int)v); return 1; }
58+inline bool luavalue_cast(int index, int16& v){ int i; bool r = luavalue_cast(index, i); v = i; return r; }
59+// uint16
60+inline int push_luavalue(uint16 v) { push_luavalue((int)v); return 1; }
61+inline bool luavalue_cast(int index, uint16& v){ int i; bool r = luavalue_cast(index, i); v = i; return r; }
62+// uint32
63+inline int push_luavalue(uint32 v) { push_luavalue((double)v); return 1; }
64+inline bool luavalue_cast(int index, uint32& v) { double d; bool r = luavalue_cast(index, d); v = d; return r; }
65+// int64
66+inline int push_luavalue(int64 v) { push_luavalue((double)v); return 1; }
67+inline bool luavalue_cast(int index, int64& v) { double d; bool r = luavalue_cast(index, d); v = d; return r; }
68+// uint64
69+inline int push_luavalue(uint64 v) { push_luavalue((double)v); return 1; }
70+inline bool luavalue_cast(int index, uint64& v) { double d; bool r = luavalue_cast(index, d); v = d; return r; }
71+
72+// string
73+int push_luavalue(const char* cstr);
74+int push_luavalue(const std::string& str);
75+bool luavalue_cast(int index, std::string& str);
76+
77+// lua cpp function
78+int push_luavalue(const LuaCppFunction& func);
79+
80+// color
81+int push_luavalue(const Color& color);
82+bool luavalue_cast(int index, Color& color);
83+
84+// rect
85+int push_luavalue(const Rect& rect);
86+bool luavalue_cast(int index, Rect& rect);
87+
88+// point
89+int push_luavalue(const Point& point);
90+bool luavalue_cast(int index, Point& point);
91+
92+// size
93+int push_luavalue(const Size& size);
94+bool luavalue_cast(int index, Size& size);
95+
96+// otml nodes
97+int push_luavalue(const OTMLNodePtr& node);
98+bool luavalue_cast(int index, OTMLNodePtr& node);
99+
100+// enum
101+template<class T>
102+typename std::enable_if<std::is_enum<T>::value, int>::type
103+push_luavalue(T e) { return push_luavalue((int)e); }
104+
105+template<class T>
106+typename std::enable_if<std::is_enum<T>::value, bool>::type
107+luavalue_cast(int index, T& myenum);
108+
109+// LuaObject pointers
110+template<class T>
111+typename std::enable_if<std::is_base_of<LuaObject, typename T::element_type>::value, int>::type
112+push_luavalue(const T& obj);
113+
114+bool luavalue_cast(int index, LuaObjectPtr& obj);
115+
116+template<class T>
117+typename std::enable_if<std::is_base_of<LuaObject, T>::value, bool>::type
118+luavalue_cast(int index, stdext::shared_object_ptr<T>& ptr);
119+
120+// std::function
121+template<typename Ret, typename... Args>
122+int push_luavalue(const std::function<Ret(Args...)>& func);
123+
124+template<typename... Args>
125+bool luavalue_cast(int index, std::function<void(Args...)>& func);
126+
127+template<typename Ret, typename... Args>
128+typename std::enable_if<!std::is_void<Ret>::value, bool>::type
129+luavalue_cast(int index, std::function<Ret(Args...)>& func);
130+
131+// list
132+template<typename T>
133+int push_luavalue(const std::list<T>& list);
134+
135+template<typename T>
136+bool luavalue_cast(int index, std::list<T>& list);
137+
138+// vector
139+template<typename T>
140+int push_luavalue(const std::vector<T>& vec);
141+
142+template<typename T>
143+bool luavalue_cast(int index, std::vector<T>& vec);
144+
145+// set
146+template<typename T>
147+int push_luavalue(const std::set<T>& vec);
148+
149+template<typename T>
150+bool luavalue_cast(int index, std::set<T>& vec);
151+
152+// deque
153+template<class T>
154+int push_luavalue(const std::deque<T>& vec);
155+
156+template<typename T>
157+bool luavalue_cast(int index, std::deque<T>& vec);
158+
159+// map
160+template<class K, class V>
161+int push_luavalue(const std::map<K, V>& map);
162+
163+template<class K, class V>
164+bool luavalue_cast(int index, std::map<K, V>& map);
165+
166+// pair
167+template<class K, class V>
168+bool luavalue_cast(int index, std::pair<K, V>& pair);
169+
170+// tuple
171+template<typename... Args>
172+int push_luavalue(const std::tuple<Args...>& tuple);
173+
174+template<typename... Args>
175+int push_internal_luavalue(const std::tuple<Args...>& tuple);
176+
177+// start definitions
178+
179+#include "luaexception.h"
180+#include "luainterface.h"
181+#include "luaobject.h"
182+
183+template<typename T>
184+int push_internal_luavalue(T v) {
185+ return push_luavalue(v);
186+}
187+
188+template<class T>
189+typename std::enable_if<std::is_enum<T>::value, bool>::type
190+luavalue_cast(int index, T& myenum) {
191+ int i;
192+ if(luavalue_cast(index, i)) {
193+ myenum = (T)i;
194+ return true;
195+ }
196+ return false;
197+}
198+
199+template<class T>
200+typename std::enable_if<std::is_base_of<LuaObject, typename T::element_type>::value, int>::type
201+push_luavalue(const T& obj) {
202+ if(obj)
203+ g_lua.pushObject(obj);
204+ else
205+ g_lua.pushNil();
206+ return 1;
207+}
208+
209+template<class T>
210+typename std::enable_if<std::is_base_of<LuaObject, T>::value, bool>::type
211+luavalue_cast(int index, stdext::shared_object_ptr<T>& ptr) {
212+ LuaObjectPtr obj;
213+ if(!luavalue_cast(index, obj))
214+ return false;
215+ if(obj)
216+ ptr = obj->dynamic_self_cast<T>();
217+ else
218+ ptr = nullptr;
219+ return true;
220+}
221+
222+template<typename Ret, typename... Args>
223+int push_luavalue(const std::function<Ret(Args...)>& func) {
224+ if(func) {
225+ LuaCppFunction f = luabinder::bind_fun(func);
226+ g_lua.pushCppFunction(f);
227+ } else
228+ g_lua.pushNil();
229+ return 1;
230+}
231+
232+template<typename... Args>
233+bool luavalue_cast(int index, std::function<void(Args...)>& func) {
234+ if(g_lua.isFunction(index)) {
235+ g_lua.pushValue(index);
236+ // weak references are used here, this means that the script must hold another reference
237+ // to this function, otherwise it will expire
238+ int funcWeakRef = g_lua.weakRef();
239+ func = [=](Args... args) {
240+ // note that we must catch exceptions, because this lambda can be called from anywhere
241+ // and most of them won't catch exceptions (e.g. dispatcher)
242+ g_lua.getWeakRef(funcWeakRef);
243+ auto error = std::make_shared<std::string>();
244+ if(g_lua.isFunction()) {
245+ int numArgs = g_lua.polymorphicPush(args...);
246+ int rets = g_lua.safeCall(numArgs, -1, error);
247+ if (!error->empty()) {
248+ g_logger.error(stdext::format("lua function callback failed: %s", *error));
249+ } else {
250+ g_lua.pop(rets);
251+ }
252+ } else {
253+ g_logger.error("attempt to call an expired lua function from C++, did you forget to hold a reference for that function?");
254+ }
255+ };
256+ return true;
257+ } else if(g_lua.isNil(index)) {
258+ func = std::function<void(Args...)>();
259+ return true;
260+ }
261+ return false;
262+}
263+
264+template<typename Ret, typename... Args>
265+typename std::enable_if<!std::is_void<Ret>::value, bool>::type
266+luavalue_cast(int index, std::function<Ret(Args...)>& func) {
267+ if(g_lua.isFunction(index)) {
268+ g_lua.pushValue(index);
269+ // weak references are used here, this means that the script must hold another reference
270+ // to this function, otherwise it will expire
271+ int funcWeakRef = g_lua.weakRef();
272+ func = [=](Args... args) -> Ret {
273+ // note that we must catch exceptions, because this lambda can be called from anywhere
274+ // and most of them won't catch exceptions (e.g. dispatcher)
275+ try {
276+ g_lua.getWeakRef(funcWeakRef);
277+ if(g_lua.isFunction()) {
278+ int numArgs = g_lua.polymorphicPush(args...);
279+ if(g_lua.safeCall(numArgs) != 1)
280+ throw LuaException("a function from lua didn't retrieve the expected number of results", 0);
281+ return g_lua.polymorphicPop<Ret>();
282+ } else {
283+ throw LuaException("attempt to call an expired lua function from C++,"
284+ "did you forget to hold a reference for that function?", 0);
285+ }
286+ } catch(LuaException& e) {
287+ g_logger.error(stdext::format("lua function callback failed: %s", e.what()));
288+ }
289+ return Ret();
290+ };
291+ return true;
292+ } else if(g_lua.isNil(index)) {
293+ func = std::function<Ret(Args...)>();
294+ return true;
295+ }
296+ return false;
297+}
298+
299+template<typename T>
300+int push_luavalue(const std::list<T>& list) {
301+ g_lua.createTable(list.size(), 0);
302+ int i = 1;
303+ for(const T& v : list) {
304+ push_internal_luavalue(v);
305+ g_lua.rawSeti(i);
306+ i++;
307+ }
308+ return 1;
309+}
310+
311+template<typename T>
312+bool luavalue_cast(int index, std::list<T>& list)
313+{
314+ if(g_lua.isTable(index)) {
315+ g_lua.pushNil();
316+ while(g_lua.next(index < 0 ? index-1 : index)) {
317+ T value;
318+ if(luavalue_cast(-1, value))
319+ list.push_back(value);
320+ g_lua.pop();
321+ }
322+ return true;
323+ }
324+ return false;
325+}
326+
327+template<typename T>
328+int push_luavalue(const std::vector<T>& vec)
329+{
330+ g_lua.createTable(vec.size(), 0);
331+ int i = 1;
332+ for (const T& v : vec) {
333+ push_internal_luavalue(v);
334+ g_lua.rawSeti(i);
335+ i++;
336+ }
337+ return 1;
338+}
339+
340+template<typename T>
341+bool luavalue_cast(int index, std::vector<T>& vec)
342+{
343+ if (g_lua.isTable(index)) {
344+ g_lua.pushNil();
345+ while (g_lua.next(index < 0 ? index - 1 : index)) {
346+ T value;
347+ if (luavalue_cast(-1, value))
348+ vec.push_back(value);
349+ g_lua.pop();
350+ }
351+ return true;
352+ }
353+ return false;
354+}
355+
356+template<typename T>
357+int push_luavalue(const std::set<T>& set)
358+{
359+ g_lua.createTable(set.size(), 0);
360+ int i = 1;
361+ for (const T& v : set) {
362+ push_internal_luavalue(v);
363+ g_lua.rawSeti(i);
364+ i++;
365+ }
366+ return 1;
367+}
368+
369+template<typename T>
370+bool luavalue_cast(int index, std::set<T>& set)
371+{
372+ if (g_lua.isTable(index)) {
373+ g_lua.pushNil();
374+ while (g_lua.next(index < 0 ? index - 1 : index)) {
375+ T value;
376+ if (luavalue_cast(-1, value))
377+ set.insert(value);
378+ g_lua.pop();
379+ }
380+ return true;
381+ }
382+ return false;
383+}
384+
385+template<typename T>
386+int push_luavalue(const std::deque<T>& vec) {
387+ g_lua.createTable(vec.size(), 0);
388+ int i = 1;
389+ for(const T& v : vec) {
390+ push_internal_luavalue(v);
391+ g_lua.rawSeti(i);
392+ i++;
393+ }
394+ return 1;
395+}
396+
397+template<typename T>
398+bool luavalue_cast(int index, std::deque<T>& vec)
399+{
400+ if(g_lua.isTable(index)) {
401+ g_lua.pushNil();
402+ while(g_lua.next(index < 0 ? index-1 : index)) {
403+ T value;
404+ if(luavalue_cast(-1, value))
405+ vec.push_back(value);
406+ g_lua.pop();
407+ }
408+ return true;
409+ }
410+ return false;
411+}
412+
413+template<class K, class V>
414+int push_luavalue(const std::map<K, V>& map)
415+{
416+ g_lua.newTable();
417+ for(auto& it : map) {
418+ push_internal_luavalue(it.first);
419+ push_internal_luavalue(it.second);
420+ g_lua.rawSet();
421+ }
422+ return 1;
423+}
424+
425+template<class K, class V>
426+bool luavalue_cast(int index, std::map<K, V>& map)
427+{
428+ if(g_lua.isTable(index)) {
429+ g_lua.pushNil();
430+ while(g_lua.next(index < 0 ? index-1 : index)) {
431+ K key;
432+ V value;
433+ if(luavalue_cast(-1, value) && luavalue_cast(-2, key))
434+ map[key] = value;
435+ g_lua.pop();
436+ }
437+ return true;
438+ }
439+ return false;
440+}
441+
442+template<class K, class V>
443+bool luavalue_cast(int index, std::pair<K, V>& pair)
444+{
445+ if (g_lua.isTable(index)) {
446+ g_lua.pushNil();
447+ if (g_lua.next(index < 0 ? index - 1 : index)) {
448+ K value;
449+ if (!luavalue_cast(-1, value))
450+ pair.first = value;
451+ g_lua.pop();
452+ } else {
453+ return false;
454+ }
455+ if (g_lua.next(index < 0 ? index - 1 : index)) {
456+ V value;
457+ if (!luavalue_cast(-1, value))
458+ pair.second = value;
459+ g_lua.pop();
460+ } else {
461+ return false;
462+ }
463+
464+ return true;
465+ }
466+ return false;
467+}
468+
469+
470+
471+template<int N>
472+struct push_tuple_internal_luavalue {
473+ template<typename Tuple>
474+ static void call(const Tuple& tuple) {
475+ push_internal_luavalue(std::get<N-1>(tuple));
476+ g_lua.rawSeti(N);
477+ push_tuple_internal_luavalue<N-1>::call(tuple);
478+ }
479+};
480+
481+template<>
482+struct push_tuple_internal_luavalue<0> {
483+ template<typename Tuple>
484+ static void call(const Tuple& tuple) { }
485+};
486+
487+template<typename... Args>
488+int push_internal_luavalue(const std::tuple<Args...>& tuple) {
489+ g_lua.newTable();
490+ push_tuple_internal_luavalue<sizeof...(Args)>::call(tuple);
491+ return 1;
492+}
493+
494+template<int N>
495+struct push_tuple_luavalue {
496+ template<typename Tuple>
497+ static void call(const Tuple& tuple) {
498+ push_internal_luavalue(std::get<std::tuple_size<Tuple>::value - N>(tuple));
499+ push_tuple_luavalue<N-1>::call(tuple);
500+ }
501+};
502+
503+template<>
504+struct push_tuple_luavalue<0> {
505+ template<typename Tuple>
506+ static void call(const Tuple& tuple) { }
507+};
508+
509+template<typename... Args>
510+int push_luavalue(const std::tuple<Args...>& tuple) {
511+ push_tuple_luavalue<sizeof...(Args)>::call(tuple);
512+ return sizeof...(Args);
513+}
514+
515+#endif
A RD_Client/src/framework/net/connection.cpp +321-0 View file
@@ -0,0 +1,321 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#include "connection.h"
24+
25+#include <framework/core/application.h>
26+#include <framework/core/eventdispatcher.h>
27+#include <boost/asio.hpp>
28+#include <framework/util/stats.h>
29+#include <framework/util/extras.h>
30+#include <chrono>
31+
32+asio::io_service g_ioService;
33+std::list<std::shared_ptr<asio::streambuf>> Connection::m_outputStreams;
34+
35+Connection::Connection() :
36+ m_readTimer(g_ioService),
37+ m_writeTimer(g_ioService),
38+ m_delayedWriteTimer(g_ioService),
39+ m_resolver(g_ioService),
40+ m_socket(g_ioService)
41+{
42+ m_connected = false;
43+ m_connecting = false;
44+}
45+
46+Connection::~Connection()
47+{
48+ VALIDATE(!g_app.isTerminated());
49+ close();
50+}
51+
52+void Connection::poll()
53+{
54+ AutoStat s(STATS_MAIN, "PollConnection");
55+ // reset must always be called prior to poll
56+ g_ioService.reset();
57+ g_ioService.poll();
58+}
59+
60+void Connection::terminate()
61+{
62+ g_ioService.stop();
63+ m_outputStreams.clear();
64+}
65+
66+void Connection::close()
67+{
68+ if(!m_connected && !m_connecting)
69+ return;
70+
71+ // flush send data before disconnecting on clean connections
72+ if(m_connected && !m_error && m_outputStream)
73+ internal_write();
74+
75+ m_connecting = false;
76+ m_connected = false;
77+ m_connectCallback = nullptr;
78+ m_errorCallback = nullptr;
79+ m_recvCallback = nullptr;
80+
81+ m_resolver.cancel();
82+ m_readTimer.cancel();
83+ m_writeTimer.cancel();
84+ m_delayedWriteTimer.cancel();
85+
86+ if(m_socket.is_open()) {
87+ boost::system::error_code ec;
88+ m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
89+ m_socket.close();
90+ }
91+}
92+
93+void Connection::connect(const std::string& host, uint16 port, const std::function<void()>& connectCallback)
94+{
95+ m_connected = false;
96+ m_connecting = true;
97+ m_error.clear();
98+ m_connectCallback = connectCallback;
99+
100+ asio::ip::tcp::resolver::query query(host, stdext::unsafe_cast<std::string>(port));
101+ m_resolver.async_resolve(query, std::bind(&Connection::onResolve, asConnection(), std::placeholders::_1, std::placeholders::_2));
102+
103+ m_readTimer.cancel();
104+ m_readTimer.expires_from_now(std::chrono::seconds(READ_TIMEOUT));
105+ m_readTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
106+}
107+
108+void Connection::internal_connect(asio::ip::basic_resolver<asio::ip::tcp>::iterator endpointIterator)
109+{
110+ m_socket.async_connect(*endpointIterator, std::bind(&Connection::onConnect, asConnection(), std::placeholders::_1));
111+
112+ m_readTimer.cancel();
113+ m_readTimer.expires_from_now(std::chrono::seconds(READ_TIMEOUT));
114+ m_readTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
115+}
116+
117+void Connection::write(uint8* buffer, size_t size)
118+{
119+ if(!m_connected)
120+ return;
121+
122+ // we can't send the data right away, otherwise we could create tcp congestion
123+ if(!m_outputStream) {
124+ if(!m_outputStreams.empty()) {
125+ m_outputStream = m_outputStreams.front();
126+ m_outputStreams.pop_front();
127+ } else
128+ m_outputStream = std::shared_ptr<asio::streambuf>(new asio::streambuf);
129+
130+ m_delayedWriteTimer.cancel();
131+ m_delayedWriteTimer.expires_from_now(std::chrono::milliseconds(0));
132+ m_delayedWriteTimer.async_wait(std::bind(&Connection::onCanWrite, asConnection(), std::placeholders::_1));
133+ }
134+
135+ std::ostream os(m_outputStream.get());
136+ os.write((const char*)buffer, size);
137+ os.flush();
138+}
139+
140+void Connection::internal_write()
141+{
142+ if(!m_connected)
143+ return;
144+
145+ std::shared_ptr<asio::streambuf> outputStream = m_outputStream;
146+ m_outputStream = nullptr;
147+
148+ asio::async_write(m_socket,
149+ *outputStream,
150+ std::bind(&Connection::onWrite, asConnection(), std::placeholders::_1, std::placeholders::_2, outputStream));
151+
152+ m_writeTimer.cancel();
153+ m_writeTimer.expires_from_now(std::chrono::seconds(WRITE_TIMEOUT));
154+ m_writeTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
155+}
156+
157+void Connection::read(uint32 bytes, const RecvCallback& callback)
158+{
159+ if(!m_connected)
160+ return;
161+
162+ m_recvCallback = callback;
163+
164+ asio::async_read(m_socket,
165+ asio::buffer(m_inputStream.prepare(bytes)),
166+ std::bind(&Connection::onRecv, asConnection(), std::placeholders::_1, std::placeholders::_2));
167+
168+ m_readTimer.cancel();
169+ m_readTimer.expires_from_now(std::chrono::seconds(READ_TIMEOUT));
170+ m_readTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
171+}
172+
173+void Connection::read_until(const std::string& what, const RecvCallback& callback)
174+{
175+ if(!m_connected)
176+ return;
177+
178+ m_recvCallback = callback;
179+
180+ asio::async_read_until(m_socket,
181+ m_inputStream,
182+ what.c_str(),
183+ std::bind(&Connection::onRecv, asConnection(), std::placeholders::_1, std::placeholders::_2));
184+
185+ m_readTimer.cancel();
186+ m_readTimer.expires_from_now(std::chrono::seconds(READ_TIMEOUT));
187+ m_readTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
188+}
189+
190+void Connection::read_some(const RecvCallback& callback)
191+{
192+ if(!m_connected)
193+ return;
194+
195+ m_recvCallback = callback;
196+
197+ m_socket.async_read_some(asio::buffer(m_inputStream.prepare(RECV_BUFFER_SIZE)),
198+ std::bind(&Connection::onRecv, asConnection(), std::placeholders::_1, std::placeholders::_2));
199+
200+ m_readTimer.cancel();
201+ m_readTimer.expires_from_now(std::chrono::seconds(READ_TIMEOUT));
202+ m_readTimer.async_wait(std::bind(&Connection::onTimeout, asConnection(), std::placeholders::_1));
203+}
204+
205+void Connection::onResolve(const boost::system::error_code& error, asio::ip::basic_resolver<asio::ip::tcp>::iterator endpointIterator)
206+{
207+ m_readTimer.cancel();
208+
209+ if(error == asio::error::operation_aborted)
210+ return;
211+
212+ if(!error)
213+ internal_connect(endpointIterator);
214+ else
215+ handleError(error);
216+}
217+
218+void Connection::onConnect(const boost::system::error_code& error)
219+{
220+ m_readTimer.cancel();
221+ m_activityTimer.restart();
222+
223+ if(error == asio::error::operation_aborted)
224+ return;
225+
226+ if(!error) {
227+ m_connected = true;
228+
229+ // disable nagle's algorithm, this make the game play smoother
230+ boost::asio::ip::tcp::no_delay option(true);
231+ m_socket.set_option(option);
232+ boost::system::error_code ecc;
233+ m_socket.set_option(boost::asio::socket_base::send_buffer_size(524288), ecc);
234+ m_socket.set_option(boost::asio::socket_base::receive_buffer_size(524288), ecc);
235+
236+ if(m_connectCallback)
237+ m_connectCallback();
238+ } else
239+ handleError(error);
240+
241+ m_connecting = false;
242+}
243+
244+void Connection::onCanWrite(const boost::system::error_code& error)
245+{
246+ m_delayedWriteTimer.cancel();
247+
248+ if(error == asio::error::operation_aborted)
249+ return;
250+
251+ if(m_connected)
252+ internal_write();
253+}
254+
255+void Connection::onWrite(const boost::system::error_code& error, size_t writeSize, std::shared_ptr<asio::streambuf> outputStream)
256+{
257+ m_writeTimer.cancel();
258+
259+ if(error == asio::error::operation_aborted)
260+ return;
261+
262+ // free output stream and store for using it again later
263+ outputStream->consume(outputStream->size());
264+ m_outputStreams.push_back(outputStream);
265+
266+ if(m_connected && error)
267+ handleError(error);
268+}
269+
270+void Connection::onRecv(const boost::system::error_code& error, size_t recvSize)
271+{
272+ m_readTimer.cancel();
273+ m_activityTimer.restart();
274+
275+ if(error == asio::error::operation_aborted)
276+ return;
277+
278+ if(m_connected) {
279+ if(!error) {
280+ if(m_recvCallback) {
281+ const char* header = boost::asio::buffer_cast<const char*>(m_inputStream.data());
282+ m_recvCallback((uint8*)header, recvSize);
283+ }
284+ } else
285+ handleError(error);
286+ }
287+
288+ if(!error)
289+ m_inputStream.consume(recvSize);
290+}
291+
292+void Connection::onTimeout(const boost::system::error_code& error)
293+{
294+ if(error == asio::error::operation_aborted)
295+ return;
296+
297+ handleError(asio::error::timed_out);
298+}
299+
300+void Connection::handleError(const boost::system::error_code& error)
301+{
302+ if(error == asio::error::operation_aborted)
303+ return;
304+
305+ m_error = error;
306+ if(m_errorCallback)
307+ m_errorCallback(error);
308+ if(m_connected || m_connecting)
309+ close();
310+}
311+
312+int Connection::getIp()
313+{
314+ boost::system::error_code error;
315+ const boost::asio::ip::tcp::endpoint ip = m_socket.remote_endpoint(error);
316+ if(!error)
317+ return boost::asio::detail::socket_ops::host_to_network_long(ip.address().to_v4().to_ulong());
318+
319+ g_logger.error("Getting remote ip");
320+ return 0;
321+}
A RD_Client/src/framework/net/connection.h +101-0 View file
@@ -0,0 +1,101 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef CONNECTION_H
24+#define CONNECTION_H
25+
26+#include "declarations.h"
27+#include <framework/luaengine/luaobject.h>
28+#include <framework/core/timer.h>
29+#include <framework/core/declarations.h>
30+
31+class Connection : public LuaObject
32+{
33+ typedef std::function<void(const boost::system::error_code&)> ErrorCallback;
34+ typedef std::function<void(uint8*, uint32)> RecvCallback;
35+
36+ static constexpr int32_t READ_TIMEOUT = 30;
37+ static constexpr int32_t WRITE_TIMEOUT = 30;
38+
39+ enum {
40+ SEND_BUFFER_SIZE = 327680,
41+ RECV_BUFFER_SIZE = 327680
42+ };
43+
44+public:
45+ Connection();
46+ ~Connection();
47+
48+ static void poll();
49+ static void terminate();
50+
51+ void connect(const std::string& host, uint16 port, const std::function<void()>& connectCallback);
52+ void close();
53+
54+ void write(uint8* buffer, size_t size);
55+ void read(uint32 bytes, const RecvCallback& callback);
56+ void read_until(const std::string& what, const RecvCallback& callback);
57+ void read_some(const RecvCallback& callback);
58+
59+ void setErrorCallback(const ErrorCallback& errorCallback) { m_errorCallback = errorCallback; }
60+
61+ int getIp();
62+ boost::system::error_code getError() { return m_error; }
63+ bool isConnecting() { return m_connecting; }
64+ bool isConnected() { return m_connected; }
65+ ticks_t getElapsedTicksSinceLastRead() { return m_connected ? m_activityTimer.elapsed_millis() : -1; }
66+
67+ ConnectionPtr asConnection() { return static_self_cast<Connection>(); }
68+
69+protected:
70+ void internal_connect(asio::ip::basic_resolver<asio::ip::tcp>::iterator endpointIterator);
71+ void internal_write();
72+ void onResolve(const boost::system::error_code& error, asio::ip::tcp::resolver::iterator endpointIterator);
73+ void onConnect(const boost::system::error_code& error);
74+ void onCanWrite(const boost::system::error_code& error);
75+ void onWrite(const boost::system::error_code& error, size_t writeSize, std::shared_ptr<asio::streambuf> outputStream);
76+ void onRecv(const boost::system::error_code& error, size_t recvSize);
77+ void onTimeout(const boost::system::error_code& error);
78+ void handleError(const boost::system::error_code& error);
79+
80+ std::function<void()> m_connectCallback;
81+ ErrorCallback m_errorCallback;
82+ RecvCallback m_recvCallback;
83+
84+ asio::steady_timer m_readTimer;
85+ asio::steady_timer m_writeTimer;
86+ asio::steady_timer m_delayedWriteTimer;
87+ asio::ip::tcp::resolver m_resolver;
88+ asio::ip::tcp::socket m_socket;
89+
90+ static std::list<std::shared_ptr<asio::streambuf>> m_outputStreams;
91+ std::shared_ptr<asio::streambuf> m_outputStream;
92+ asio::streambuf m_inputStream;
93+ bool m_connected;
94+ bool m_connecting;
95+ boost::system::error_code m_error;
96+ stdext::timer m_activityTimer;
97+
98+ friend class Server;
99+};
100+
101+#endif
A RD_Client/src/framework/net/declarations.h +46-0 View file
@@ -0,0 +1,46 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef FRAMEWORK_NET_DECLARATIONS_H
24+#define FRAMEWORK_NET_DECLARATIONS_H
25+
26+#include <framework/global.h>
27+
28+namespace asio = boost::asio;
29+
30+class InputMessage;
31+class OutputMessage;
32+class Connection;
33+class Protocol;
34+class Server;
35+class PacketPlayer;
36+class PacketRecorder;
37+
38+typedef stdext::shared_object_ptr<InputMessage> InputMessagePtr;
39+typedef stdext::shared_object_ptr<OutputMessage> OutputMessagePtr;
40+typedef stdext::shared_object_ptr<Connection> ConnectionPtr;
41+typedef stdext::shared_object_ptr<Protocol> ProtocolPtr;
42+typedef stdext::shared_object_ptr<Server> ServerPtr;
43+typedef stdext::shared_object_ptr<PacketPlayer> PacketPlayerPtr;
44+typedef stdext::shared_object_ptr<PacketRecorder> PacketRecorderPtr;
45+
46+#endif
A RD_Client/src/framework/net/inputmessage.cpp +152-0 View file
@@ -0,0 +1,152 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#include "inputmessage.h"
24+#include <framework/util/crypt.h>
25+#include <client/map.h>
26+
27+InputMessage::InputMessage()
28+{
29+ reset();
30+}
31+
32+void InputMessage::reset()
33+{
34+ m_messageSize = 0;
35+ m_readPos = MAX_HEADER_SIZE;
36+ m_headerPos = MAX_HEADER_SIZE;
37+}
38+
39+void InputMessage::setBuffer(const std::string& buffer)
40+{
41+ int len = buffer.size();
42+ checkWrite(MAX_HEADER_SIZE + len);
43+ memcpy(m_buffer + MAX_HEADER_SIZE, buffer.c_str(), len);
44+ m_readPos = MAX_HEADER_SIZE;
45+ m_headerPos = MAX_HEADER_SIZE;
46+ m_messageSize = len;
47+}
48+
49+uint8 InputMessage::getU8()
50+{
51+ checkRead(1);
52+ uint8 v = m_buffer[m_readPos];
53+ m_readPos += 1;
54+ return v;
55+}
56+
57+uint16 InputMessage::getU16()
58+{
59+ checkRead(2);
60+ uint16 v = stdext::readULE16(m_buffer + m_readPos);
61+ m_readPos += 2;
62+ return v;
63+}
64+
65+uint32 InputMessage::getU32()
66+{
67+ checkRead(4);
68+ uint32 v = stdext::readULE32(m_buffer + m_readPos);
69+ m_readPos += 4;
70+ return v;
71+}
72+
73+uint64 InputMessage::getU64()
74+{
75+ checkRead(8);
76+ uint64 v = stdext::readULE64(m_buffer + m_readPos);
77+ m_readPos += 8;
78+ return v;
79+}
80+
81+std::string InputMessage::getString()
82+{
83+ uint16 stringLength = getU16();
84+ checkRead(stringLength);
85+ char* v = (char*)(m_buffer + m_readPos);
86+ m_readPos += stringLength;
87+ return std::string(v, stringLength);
88+}
89+
90+double InputMessage::getDouble()
91+{
92+ uint8 precision = getU8();
93+ int32 v = getU32() - INT_MAX;
94+ return (v / std::pow((float)10, precision));
95+}
96+
97+bool InputMessage::decryptRsa(int size)
98+{
99+ checkRead(size);
100+ g_crypt.rsaDecrypt((unsigned char*)m_buffer + m_readPos, size);
101+ return (getU8() == 0x00);
102+}
103+
104+void InputMessage::fillBuffer(uint8 *buffer, uint32 size)
105+{
106+ checkWrite(m_readPos + size);
107+ memcpy(m_buffer + m_readPos, buffer, size);
108+ m_messageSize += size;
109+}
110+
111+void InputMessage::setHeaderSize(uint32 size)
112+{
113+ VALIDATE(MAX_HEADER_SIZE >= size);
114+ m_headerPos = MAX_HEADER_SIZE - size;
115+ m_readPos = m_headerPos;
116+}
117+
118+bool InputMessage::readChecksum()
119+{
120+ uint32 receivedCheck = getU32();
121+ uint32 checksum = stdext::adler32(m_buffer + m_readPos, getUnreadSize());
122+ return receivedCheck == checksum;
123+}
124+
125+bool InputMessage::canRead(int bytes)
126+{
127+ if((m_readPos - m_headerPos + bytes > m_messageSize) || (m_readPos + bytes > BUFFER_MAXSIZE))
128+ return false;
129+ return true;
130+}
131+void InputMessage::checkRead(int bytes)
132+{
133+ if(!canRead(bytes))
134+ throw stdext::exception("InputMessage eof reached");
135+}
136+
137+void InputMessage::checkWrite(int bytes)
138+{
139+ if(bytes > BUFFER_MAXSIZE)
140+ throw stdext::exception("InputMessage max buffer size reached");
141+}
142+
143+void InputMessage::addZlibFooter()
144+{
145+ if (m_messageSize + 4 > BUFFER_MAXSIZE)
146+ return;
147+ m_buffer[m_messageSize + m_headerPos] = 0x00;
148+ m_buffer[m_messageSize + m_headerPos + 1] = 0x00;
149+ m_buffer[m_messageSize + m_headerPos + 2] = 0xFF;
150+ m_buffer[m_messageSize + m_headerPos + 3] = 0xFF;
151+ m_messageSize += 4;
152+}
A RD_Client/src/framework/net/inputmessage.h +98-0 View file
@@ -0,0 +1,98 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef INPUTMESSAGE_H
24+#define INPUTMESSAGE_H
25+
26+#include "declarations.h"
27+#include <framework/luaengine/luaobject.h>
28+
29+// @bindclass
30+class InputMessage : public LuaObject
31+{
32+public:
33+ enum {
34+ BUFFER_MAXSIZE = 327680,
35+ MAX_HEADER_SIZE = 12
36+ };
37+
38+ InputMessage();
39+
40+ void setBuffer(const std::string& buffer);
41+ std::string getBuffer() { return std::string((char*)m_buffer + m_headerPos, m_messageSize); }
42+ std::string getBodyBuffer() { return std::string((char*)m_buffer + MAX_HEADER_SIZE, m_messageSize - getHeaderSize()); }
43+
44+ void skipBytes(uint32 bytes) { m_readPos += bytes; }
45+ void setReadPos(uint32 readPos) { m_readPos = readPos; }
46+ uint8 getU8();
47+ uint16 getU16();
48+ uint32 getU32();
49+ uint64 getU64();
50+ std::string getString();
51+ double getDouble();
52+
53+ uint8 peekU8() { uint8 v = getU8(); m_readPos-=1; return v; }
54+ uint16 peekU16() { uint16 v = getU16(); m_readPos-=2; return v; }
55+ uint32 peekU32() { uint32 v = getU32(); m_readPos-=4; return v; }
56+ uint64 peekU64() { uint64 v = getU64(); m_readPos-=8; return v; }
57+
58+ bool decryptRsa(int size);
59+
60+ uint32 getHeaderPos() { return m_headerPos; }
61+ uint32 getHeaderSize() { return (MAX_HEADER_SIZE - m_headerPos); }
62+ int getReadSize() { return m_readPos - m_headerPos; }
63+ int getReadPos() { return m_readPos; }
64+ int getUnreadSize() { return m_messageSize - (m_readPos - m_headerPos); }
65+ uint32 getMessageSize() { return m_messageSize; }
66+
67+ bool eof() { return (m_readPos - m_headerPos) >= m_messageSize; }
68+
69+protected:
70+ void reset();
71+ void fillBuffer(uint8 *buffer, uint32 size);
72+
73+ void setHeaderSize(uint32 size);
74+ void setMessageSize(uint32 size) { m_messageSize = size; }
75+
76+ uint8* getReadBuffer() { return m_buffer + m_readPos; }
77+ uint8* getHeaderBuffer() { return m_buffer + m_headerPos; }
78+ uint8* getDataBuffer() { return m_buffer + MAX_HEADER_SIZE; }
79+
80+ uint32 readSize(bool bigSize) { return bigSize ? getU32() : getU16(); }
81+ bool readChecksum();
82+
83+ void addZlibFooter();
84+
85+ friend class Protocol;
86+
87+private:
88+ bool canRead(int bytes);
89+ void checkRead(int bytes);
90+ void checkWrite(int bytes);
91+
92+ uint32 m_headerPos;
93+ uint32 m_readPos;
94+ uint32 m_messageSize;
95+ uint8 m_buffer[BUFFER_MAXSIZE];
96+};
97+
98+#endif
A RD_Client/src/framework/net/outputmessage.cpp +164-0 View file
@@ -0,0 +1,164 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#include <framework/net/outputmessage.h>
24+#include <framework/util/crypt.h>
25+
26+OutputMessage::OutputMessage()
27+{
28+ reset();
29+}
30+
31+void OutputMessage::reset()
32+{
33+ m_writePos = MAX_HEADER_SIZE;
34+ m_headerPos = MAX_HEADER_SIZE;
35+ m_messageSize = 0;
36+}
37+
38+void OutputMessage::setBuffer(const std::string& buffer)
39+{
40+ int len = buffer.size();
41+ reset();
42+ checkWrite(len);
43+ memcpy((char*)(m_buffer + m_writePos), buffer.c_str(), len);
44+ m_writePos += len;
45+ m_messageSize += len;
46+}
47+
48+void OutputMessage::addU8(uint8 value)
49+{
50+ checkWrite(1);
51+ m_buffer[m_writePos] = value;
52+ m_writePos += 1;
53+ m_messageSize += 1;
54+}
55+
56+void OutputMessage::addU16(uint16 value)
57+{
58+ checkWrite(2);
59+ stdext::writeULE16(m_buffer + m_writePos, value);
60+ m_writePos += 2;
61+ m_messageSize += 2;
62+}
63+
64+void OutputMessage::addU32(uint32 value)
65+{
66+ checkWrite(4);
67+ stdext::writeULE32(m_buffer + m_writePos, value);
68+ m_writePos += 4;
69+ m_messageSize += 4;
70+}
71+
72+void OutputMessage::addU64(uint64 value)
73+{
74+ checkWrite(8);
75+ stdext::writeULE64(m_buffer + m_writePos, value);
76+ m_writePos += 8;
77+ m_messageSize += 8;
78+}
79+
80+void OutputMessage::addString(const std::string& buffer)
81+{
82+ int len = buffer.length();
83+ if (len > MAX_STRING_LENGTH)
84+ throw stdext::exception(stdext::format("string length > %d", MAX_STRING_LENGTH));
85+ checkWrite(len + 2);
86+ addU16(len);
87+ memcpy((char*)(m_buffer + m_writePos), buffer.c_str(), len);
88+ m_writePos += len;
89+ m_messageSize += len;
90+}
91+
92+void OutputMessage::addRawString(const std::string& buffer)
93+{
94+ int len = buffer.length();
95+ if (len > MAX_STRING_LENGTH)
96+ throw stdext::exception(stdext::format("string length > %d", MAX_STRING_LENGTH));
97+ checkWrite(len);
98+ memcpy((char*)(m_buffer + m_writePos), buffer.c_str(), len);
99+ m_writePos += len;
100+ m_messageSize += len;
101+}
102+
103+void OutputMessage::addPaddingBytes(int bytes, uint8 byte)
104+{
105+ if(bytes <= 0)
106+ return;
107+ checkWrite(bytes);
108+ memset((void*)&m_buffer[m_writePos], byte, bytes);
109+ m_writePos += bytes;
110+ m_messageSize += bytes;
111+}
112+
113+void OutputMessage::encryptRsa()
114+{
115+ uint32_t size = g_crypt.rsaGetSize();
116+ if(m_messageSize < size)
117+ throw stdext::exception("insufficient bytes in buffer to encrypt");
118+
119+ if(!g_crypt.rsaEncrypt((unsigned char*)m_buffer + m_writePos - size, size))
120+ throw stdext::exception("rsa encryption failed");
121+}
122+
123+void OutputMessage::writeChecksum()
124+{
125+ uint32 checksum = stdext::adler32(m_buffer + m_headerPos, m_messageSize);
126+ VALIDATE(m_headerPos >= 4);
127+ m_headerPos -= 4;
128+ stdext::writeULE32(m_buffer + m_headerPos, checksum);
129+ m_messageSize += 4;
130+}
131+
132+void OutputMessage::writeSequence(uint32_t sequence)
133+{
134+ VALIDATE(m_headerPos >= 4);
135+ m_headerPos -= 4;
136+ stdext::writeULE32(m_buffer + m_headerPos, sequence);
137+ m_messageSize += 4;
138+}
139+
140+
141+void OutputMessage::writeMessageSize(bool bigSize)
142+{
143+ VALIDATE(m_headerPos >= (bigSize ? 4 : 2));
144+ m_headerPos -= (bigSize ? 4 : 2);
145+ if (bigSize) {
146+ stdext::writeULE32(m_buffer + m_headerPos, m_messageSize);
147+ } else {
148+ stdext::writeULE16(m_buffer + m_headerPos, m_messageSize);
149+ }
150+ m_messageSize += (bigSize ? 4 : 2);
151+}
152+
153+bool OutputMessage::canWrite(int bytes)
154+{
155+ if(m_writePos + bytes > BUFFER_MAXSIZE)
156+ return false;
157+ return true;
158+}
159+
160+void OutputMessage::checkWrite(int bytes)
161+{
162+ if(!canWrite(bytes))
163+ throw stdext::exception("OutputMessage max buffer size reached");
164+}
A RD_Client/src/framework/net/outputmessage.h +84-0 View file
@@ -0,0 +1,84 @@
1+/*
2+ * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient>
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE.
21+ */
22+
23+#ifndef OUTPUTMESSAGE_H
24+#define OUTPUTMESSAGE_H
25+
26+#include "declarations.h"
27+#include <framework/luaengine/luaobject.h>
28+
29+// @bindclass
30+class OutputMessage : public LuaObject
31+{
32+public:
33+ enum {
34+ BUFFER_MAXSIZE = 327680,
35+ MAX_STRING_LENGTH = 65536,
36+ MAX_HEADER_SIZE = 12
37+ };
38+
39+ OutputMessage();
40+
41+ void reset();
42+
43+ void setBuffer(const std::string& buffer);
44+ std::string getBuffer() { return std::string((char*)m_buffer + m_headerPos, m_messageSize); }
45+
46+ void addU8(uint8 value);
47+ void addU16(uint16 value);
48+ void addU32(uint32 value);
49+ void addU64(uint64 value);
50+ void addString(const std::string& buffer);
51+ void addRawString(const std::string& buffer);
52+ void addPaddingBytes(int bytes, uint8 byte = 0);
53+
54+ void encryptRsa();
55+
56+ uint32 getWritePos() { return m_writePos; }
57+ uint32 getMessageSize() { return m_messageSize; }
58+
59+ void setWritePos(uint32 writePos) { m_writePos = writePos; }
60+ void setMessageSize(uint32 messageSize) { m_messageSize = messageSize; }
61+
62+protected:
63+ uint8* getWriteBuffer() { return m_buffer + m_writePos; }
64+ uint8* getHeaderBuffer() { return m_buffer + m_headerPos; }
65+ uint8* getDataBuffer() { return m_buffer + MAX_HEADER_SIZE; }
66+
67+ void writeChecksum();
68+ void writeSequence(uint32_t sequence);
69+ void writeMessageSize(bool bigSize);
70+
71+ friend class Protocol;
72+ friend class PacketPlayer;
73+
74+private:
75+ bool canWrite(int bytes);
76+ void checkWrite(int bytes);
77+
78+ uint32 m_headerPos;
79+ uint32 m_writePos;
80+ uint32 m_messageSize;
81+ uint8 m_buffer[BUFFER_MAXSIZE];
82+};
83+
84+#endif
A RD_Client/src/framework/net/packet_player.cpp +79-0 View file
@@ -0,0 +1,79 @@
1+#include <framework/global.h>
2+#include <framework/core/clock.h>
3+
4+#include "packet_player.h"
5+
6+PacketPlayer::~PacketPlayer()
7+{
8+ if (m_event)
9+ m_event->cancel();
10+}
11+
12+PacketPlayer::PacketPlayer(const std::string& file)
13+{
14+ static uint32_t sessionId = 1;
15+#ifdef ANDROID
16+ std::ifstream f(std::string("records/") + file);
17+#else
18+ std::ifstream f(std::filesystem::path("records") / file);
19+#endif
20+ if (!f.is_open())
21+ return;
22+ std::string type, packetHex;
23+ ticks_t time;
24+ while (f >> type >> time >> packetHex) {
25+ std::string packetStr = boost::algorithm::unhex(packetHex);
26+ auto packet = std::make_shared<std::vector<uint8_t>>(packetStr.begin(), packetStr.end());
27+ if (type == "<") {
28+ m_input.push_back(std::make_pair(time, packet));
29+ } else if (type == ">") {
30+ m_output.push_back(std::make_pair(time, packet));
31+ }
32+ }
33+}
34+
35+void PacketPlayer::start(std::function<void(std::shared_ptr<std::vector<uint8_t>>)> recvCallback,
36+ std::function<void(boost::system::error_code)> disconnectCallback)
37+{
38+ m_start = g_clock.millis();
39+ m_recvCallback = recvCallback;
40+ m_disconnectCallback = disconnectCallback;
41+ m_event = g_dispatcher.scheduleEvent(std::bind(&PacketPlayer::process, this), 50);
42+}
43+
44+void PacketPlayer::stop()
45+{
46+ if (m_event)
47+ m_event->cancel();
48+ m_event = nullptr;
49+}
50+
51+void PacketPlayer::onOutputPacket(const OutputMessagePtr& packet)
52+{
53+ if (packet->getDataBuffer()[0] == 0x14) { // logout
54+ m_disconnectCallback(boost::asio::error::eof);
55+ stop();
56+ }
57+}
58+
59+
60+void PacketPlayer::process()
61+{
62+ ticks_t nextPacket = 1;
63+ while (!m_input.empty()) {
64+ auto& packet = m_input.front();
65+ nextPacket = (packet.first + m_start) - g_clock.millis();
66+ if (nextPacket > 1)
67+ break;
68+ m_recvCallback(packet.second);
69+ m_input.pop_front();
70+ }
71+
72+ if (!m_input.empty() && nextPacket > 1) {
73+ m_event = g_dispatcher.scheduleEvent(std::bind(&PacketPlayer::process, this), nextPacket);
74+ } else {
75+ m_disconnectCallback(boost::asio::error::eof);
76+ stop();
77+ }
78+}
79+
A RD_Client/src/framework/net/packet_player.h +26-0 View file
@@ -0,0 +1,26 @@
1+#pragma once
2+
3+#include <deque>
4+#include <framework/core/eventdispatcher.h>
5+#include <framework/net/outputmessage.h>
6+
7+class PacketPlayer : public LuaObject {
8+public:
9+ PacketPlayer(const std::string& file);
10+ virtual ~PacketPlayer();
11+
12+ void start(std::function<void(std::shared_ptr<std::vector<uint8_t>>)> recvCallback, std::function<void(boost::system::error_code)> disconnectCallback);
13+ void stop();
14+
15+ void onOutputPacket(const OutputMessagePtr& packet);
16+
17+private:
18+ void process();
19+
20+ ticks_t m_start;
21+ ScheduledEventPtr m_event;
22+ std::deque<std::pair<ticks_t, std::shared_ptr<std::vector<uint8_t>>>> m_input;
23+ std::deque<std::pair<ticks_t, std::shared_ptr<std::vector<uint8_t>>>> m_output;
24+ std::function<void(std::shared_ptr<std::vector<uint8_t>>)> m_recvCallback;
25+ std::function<void(boost::system::error_code)> m_disconnectCallback;
26+};
A RD_Client/src/framework/net/packet_recorder.cpp +47-0 View file
@@ -0,0 +1,47 @@
1+#include <framework/global.h>
2+#include <framework/core/clock.h>
3+#include <framework/core/resourcemanager.h>
4+
5+#include "packet_recorder.h"
6+
7+PacketRecorder::PacketRecorder(const std::string& file)
8+{
9+ m_start = g_clock.millis();
10+#ifdef ANDROID
11+ g_resources.makeDir("records");
12+ m_stream = std::ofstream(std::string("records/") + file);
13+#else
14+ std::error_code ec;
15+ std::filesystem::create_directory("records", ec);
16+ m_stream = std::ofstream(std::filesystem::path("records") / file);
17+#endif
18+}
19+
20+PacketRecorder::~PacketRecorder()
21+{
22+
23+}
24+
25+void PacketRecorder::addInputPacket(const InputMessagePtr& packet)
26+{
27+ m_stream << "< " << (g_clock.millis() - m_start) << " ";
28+ for (auto& buffer : packet->getBodyBuffer()) {
29+ m_stream << std::setfill('0') << std::setw(2) << std::hex << (uint16_t)(uint8_t)buffer;
30+ }
31+ m_stream << std::dec << "\n";
32+}
33+
34+void PacketRecorder::addOutputPacket(const OutputMessagePtr& packet)
35+{
36+ if (m_firstOutput) {
37+ // skip packet with login and password
38+ m_firstOutput = false;
39+ return;
40+ }
41+
42+ m_stream << "> " << (g_clock.millis() - m_start) << " ";
43+ for (auto& buffer : packet->getBuffer()) {
44+ m_stream << std::setfill('0') << std::setw(2) << std::hex << (uint16_t)(uint8_t)buffer;
45+ }
46+ m_stream << std::dec << "\n";
47+}
A RD_Client/src/framework/net/packet_recorder.h +18-0 View file
@@ -0,0 +1,18 @@
1+#pragma once
2+
3+#include <framework/net/inputmessage.h>
4+#include <framework/net/outputmessage.h>
5+
6+class PacketRecorder : public LuaObject {
7+public:
8+ PacketRecorder(const std::string& file);
9+ virtual ~PacketRecorder();
10+
11+ void addInputPacket(const InputMessagePtr& packet);
12+ void addOutputPacket(const OutputMessagePtr& packet);
13+
14+private:
15+ ticks_t m_start;
16+ std::ofstream m_stream;
17+ bool m_firstOutput = true;
18+};
Top