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/graphics/texture.cpp +233-0 View file
@@ -0,0 +1,233 @@
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 "texture.h"
24+#include "graphics.h"
25+#include "framebuffer.h"
26+#include "image.h"
27+
28+#include <framework/core/application.h>
29+#include <framework/core/eventdispatcher.h>
30+#include <framework/util/stats.h>
31+#include <framework/graphics/texturemanager.h>
32+
33+uint Texture::uniqueId = 1;
34+
35+Texture::Texture(const Size& size, bool depthTexture, bool smooth, bool upsideDown)
36+{
37+ m_uniqueId = uniqueId++;
38+ m_smooth = smooth;
39+ m_upsideDown = upsideDown;
40+ setupSize(size);
41+ m_needsUpdate = true;
42+
43+ g_stats.addTexture();
44+}
45+
46+Texture::Texture(const ImagePtr& image, bool buildMipmaps, bool compress, bool smooth)
47+{
48+ if (!image) {
49+ g_logger.fatal("Texture can't be created with null image!");
50+ }
51+
52+ m_uniqueId = uniqueId++;
53+ m_smooth = smooth;
54+ m_image = image;
55+ setupSize(m_image->getSize());
56+ m_needsUpdate = true;
57+
58+ g_stats.addTexture();
59+}
60+
61+Texture::~Texture()
62+{
63+ VALIDATE(!g_app.isTerminated());
64+ if (m_id != 0) { // free texture from gl memory
65+ GLuint textureId = m_id;
66+ g_graphicsDispatcher.addEvent([textureId] {
67+ glDeleteTextures(1, &textureId);
68+ });
69+ }
70+
71+ g_stats.removeTexture();
72+}
73+
74+void Texture::replace(const ImagePtr& image)
75+{
76+ m_uniqueId = uniqueId++;
77+ if (m_id != 0) { // free existing texture from gl memory
78+ GLuint textureId = m_id;
79+ g_graphicsDispatcher.addEvent([textureId] {
80+ glDeleteTextures(1, &textureId);
81+ });
82+ }
83+ if (!image) {
84+ g_logger.fatal("Texture can't be replaced with null image!");
85+ }
86+ m_id = 0;
87+ m_image = image;
88+ setupSize(m_image->getSize());
89+ m_needsUpdate = true;
90+}
91+
92+void Texture::resize(const Size& size)
93+{
94+ if(m_id == 0)
95+ update();
96+ setupSize(size);
97+ glBindTexture(GL_TEXTURE_2D, m_id);
98+ setupPixels(0, m_size, nullptr, 4);
99+ m_needsUpdate = true;
100+ update();
101+}
102+
103+
104+void Texture::update()
105+{
106+ if (m_id == 0) {
107+ glGenTextures(1, &m_id);
108+ VALIDATE(m_id != 0);
109+ glBindTexture(GL_TEXTURE_2D, m_id);
110+ if (m_image) {
111+ setupSize(m_image->getSize());
112+ int level = 0;
113+ do {
114+ setupPixels(level++, m_image->getSize(), m_image->getPixelData(), m_image->getBpp());
115+ } while (m_buildHardwareMipmaps && m_image->nextMipmap());
116+ } else {
117+ setupPixels(0, m_size, nullptr, 4);
118+ }
119+ m_image = nullptr; // free image
120+ m_needsUpdate = true;
121+ g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__);
122+ }
123+
124+ if (m_needsUpdate) {
125+ glBindTexture(GL_TEXTURE_2D, m_id);
126+ setupWrap();
127+ setupFilters();
128+ setupTranformMatrix();
129+ m_needsUpdate = false;
130+ g_graphics.checkForError(__FUNCTION__, __FILE__, __LINE__);
131+ }
132+}
133+
134+bool Texture::buildHardwareMipmaps()
135+{
136+ m_buildHardwareMipmaps = true;
137+ return true;
138+}
139+
140+void Texture::setSmooth(bool smooth)
141+{
142+ if(smooth == m_smooth)
143+ return;
144+
145+ m_smooth = smooth;
146+ m_needsUpdate = true;
147+}
148+
149+void Texture::setRepeat(bool repeat)
150+{
151+ if(m_repeat == repeat)
152+ return;
153+
154+ m_repeat = repeat;
155+ m_needsUpdate = true;
156+}
157+
158+void Texture::setUpsideDown(bool upsideDown)
159+{
160+ if(m_upsideDown == upsideDown)
161+ return;
162+ m_upsideDown = upsideDown;
163+ m_needsUpdate = true;
164+}
165+
166+void Texture::setupSize(const Size& size)
167+{
168+ if (size.width() > g_graphics.getMaxTextureSize() || size.height() > g_graphics.getMaxTextureSize()) {
169+ g_logger.fatal(stdext::format("Tried to create texture with size %ix%i while maximum texture size is %ix%i",
170+ size.width(), size.height(), g_graphics.getMaxTextureSize(), g_graphics.getMaxTextureSize()));
171+ }
172+ m_size = size;
173+}
174+
175+void Texture::setupWrap()
176+{
177+ int texParam = GL_REPEAT;
178+ if(!m_repeat)
179+ texParam = GL_CLAMP_TO_EDGE;
180+
181+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texParam);
182+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texParam);
183+}
184+
185+void Texture::setupFilters()
186+{
187+ int minFilter;
188+ int magFilter;
189+ if(m_smooth) {
190+ minFilter = m_hasMipmaps ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR;
191+ magFilter = GL_LINEAR;
192+ } else {
193+ minFilter = m_hasMipmaps ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST;
194+ magFilter = GL_NEAREST;
195+ }
196+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
197+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
198+}
199+
200+void Texture::setupTranformMatrix()
201+{
202+ if(m_upsideDown) {
203+ m_transformMatrix = { 1.0f/m_size.width(), 0.0f, 0.0f,
204+ 0.0f, -1.0f/m_size.height(), 0.0f,
205+ 0.0f, m_size.height()/(float)m_size.height(), 1.0f };
206+ } else {
207+ m_transformMatrix = { 1.0f/m_size.width(), 0.0f, 0.0f,
208+ 0.0f, 1.0f/m_size.height(), 0.0f,
209+ 0.0f, 0.0f, 1.0f };
210+ }
211+}
212+
213+void Texture::setupPixels(int level, const Size& size, uchar* pixels, int channels, bool compress)
214+{
215+ GLenum format = 0;
216+ switch(channels) {
217+ case 4:
218+ format = GL_RGBA;
219+ break;
220+ case 3:
221+ format = GL_RGB;
222+ break;
223+ case 2:
224+ format = GL_LUMINANCE_ALPHA;
225+ break;
226+ case 1:
227+ format = GL_LUMINANCE;
228+ break;
229+ }
230+
231+ GLenum internalFormat = GL_RGBA;
232+ glTexImage2D(GL_TEXTURE_2D, level, internalFormat, size.width(), size.height(), 0, format, GL_UNSIGNED_BYTE, pixels);
233+}
A RD_Client/src/framework/graphics/texture.h +89-0 View file
@@ -0,0 +1,89 @@
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 TEXTURE_H
24+#define TEXTURE_H
25+
26+#include "declarations.h"
27+
28+class Texture : public stdext::shared_object
29+{
30+ static uint uniqueId;
31+public:
32+ Texture(const Size& size, bool depthTexture = false, bool smooth = false, bool upsideDown = false);
33+ Texture(const ImagePtr& image, bool buildMipmaps = false, bool compress = false, bool smooth = false);
34+ virtual ~Texture();
35+ virtual void replace(const ImagePtr& image);
36+ void resize(const Size& size);
37+
38+ // update must be called always before drawing, only this function can use opengl functions
39+ virtual void update();
40+
41+ virtual void setUpsideDown(bool upsideDown);
42+ virtual void setSmooth(bool smooth);
43+ virtual void setRepeat(bool repeat);
44+ virtual bool buildHardwareMipmaps();
45+ void setTime(ticks_t time) { m_time = time; }
46+ void setCanCache(bool canCache)
47+ {
48+ m_canCache = canCache;
49+ }
50+
51+ uint getId() { return m_id; }
52+ uint getUniqueId() { return m_uniqueId; }
53+ ticks_t getTime() { return m_time; }
54+ int getWidth() { return m_size.width(); }
55+ int getHeight() { return m_size.height(); }
56+ const Size& getSize() { return m_size; }
57+ const Matrix3& getTransformMatrix() { return m_transformMatrix; }
58+ bool isEmpty() { return false; }
59+ bool hasRepeat() { return m_repeat; }
60+ bool hasMipmaps() { return m_hasMipmaps; }
61+ bool canCache() { return m_canCache; }
62+ virtual bool isAnimatedTexture() { return false; }
63+
64+protected:
65+
66+ void uploadPixels(const ImagePtr& image, bool buildMipmaps = false, bool compress = false);
67+
68+ void setupSize(const Size& size);
69+ void setupWrap();
70+ void setupFilters();
71+ void setupTranformMatrix();
72+ void setupPixels(int level, const Size& size, uchar *pixels, int channels = 4, bool compress = false);
73+
74+ uint m_id = 0;
75+ uint m_uniqueId = 0;
76+ ticks_t m_time = 0;
77+ Size m_size;
78+ Matrix3 m_transformMatrix;
79+ bool m_hasMipmaps = false;
80+ bool m_smooth = false;
81+ bool m_upsideDown = false;
82+ bool m_repeat = false;
83+ bool m_buildHardwareMipmaps = false;
84+ bool m_needsUpdate = false;
85+ bool m_canCache = true;
86+ ImagePtr m_image;
87+};
88+
89+#endif
A RD_Client/src/framework/graphics/texturemanager.cpp +147-0 View file
@@ -0,0 +1,147 @@
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 "texturemanager.h"
24+#include "animatedtexture.h"
25+#include "graphics.h"
26+#include "image.h"
27+
28+#include <framework/core/resourcemanager.h>
29+#include <framework/core/clock.h>
30+#include <framework/core/eventdispatcher.h>
31+#include <framework/graphics/apngloader.h>
32+#include <framework/util/stats.h>
33+
34+TextureManager g_textures;
35+
36+void TextureManager::init()
37+{
38+}
39+
40+void TextureManager::terminate()
41+{
42+ m_textures.clear();
43+ m_animatedTextures.clear();
44+}
45+
46+void TextureManager::clearCache()
47+{
48+ m_animatedTextures.clear();
49+ m_textures.clear();
50+}
51+
52+void TextureManager::reload()
53+{
54+ for(auto& it : m_textures) {
55+ const std::string& path = g_resources.guessFilePath(it.first, "png");
56+ const TexturePtr& tex = it.second;
57+
58+ ImagePtr image = Image::load(path);
59+ if(!image)
60+ continue;
61+ tex->replace(image);
62+ tex->setTime(stdext::time());
63+ }
64+}
65+
66+TexturePtr TextureManager::getTexture(const std::string& fileName)
67+{
68+ auto it = m_textures.find(fileName);
69+ if (it != m_textures.end()) {
70+ return it->second;
71+ }
72+
73+ TexturePtr texture;
74+
75+ // before must resolve filename to full path
76+ std::string filePath = g_resources.resolvePath(fileName);
77+
78+ // check if the texture is already loaded
79+ it = m_textures.find(filePath);
80+ if(it != m_textures.end()) {
81+ texture = it->second;
82+ }
83+
84+ // texture not found, load it
85+ if(!texture) {
86+ try {
87+ std::string filePathEx = g_resources.guessFilePath(filePath, "png");
88+
89+ // load texture file data
90+ std::stringstream fin;
91+ g_resources.readFileStream(filePathEx, fin);
92+ texture = loadTexture(fin, filePath);
93+ } catch(stdext::exception& e) {
94+ g_logger.error(stdext::format("Unable to load texture '%s': %s", fileName, e.what()));
95+ texture = nullptr;
96+ }
97+
98+ if(texture) {
99+ texture->setTime(stdext::time());
100+ texture->setSmooth(true);
101+ m_textures[filePath] = texture;
102+ m_textures[fileName] = texture;
103+ }
104+ }
105+
106+ return texture;
107+}
108+
109+TexturePtr TextureManager::loadTexture(std::stringstream& file, const std::string& source)
110+{
111+ TexturePtr texture;
112+
113+ apng_data apng;
114+ if(load_apng(file, &apng) == 0) {
115+ Size imageSize(apng.width, apng.height);
116+#ifndef NDEBUG
117+ if ((apng.width > 512 || apng.height > 512) && source.find("background") == std::string::npos) {
118+ // this warnining is disabled for background image
119+ g_logger.warning(stdext::format("Texture %s has size %ix%i. Too keep highest performance you shouldn't use textures bigger than 512x512 (they can't be cached)", source, apng.width, apng.height));
120+ }
121+#endif
122+ if(apng.num_frames > 1) { // animated texture
123+ std::vector<ImagePtr> frames;
124+ std::vector<int> framesDelay;
125+ for(uint i=0;i<apng.num_frames;++i) {
126+ uchar *frameData = apng.pdata + ((apng.first_frame+i) * imageSize.area() * apng.bpp);
127+ int frameDelay = apng.frames_delay[i];
128+
129+ framesDelay.push_back(frameDelay);
130+ frames.push_back(ImagePtr(new Image(imageSize, apng.bpp, frameData)));
131+ }
132+ AnimatedTexturePtr animatedTexture = new AnimatedTexture(imageSize, frames, framesDelay);
133+ m_animatedTextures.push_back(animatedTexture);
134+ texture = animatedTexture;
135+ } else {
136+ ImagePtr image = ImagePtr(new Image(imageSize, apng.bpp, apng.pdata));
137+ if (!image) {
138+ g_logger.error(stdext::format("Can't load texture: %s", source));
139+ } else {
140+ texture = TexturePtr(new Texture(image));
141+ }
142+ }
143+ free_apng(&apng);
144+ }
145+
146+ return texture;
147+}
A RD_Client/src/framework/graphics/texturemanager.h +51-0 View file
@@ -0,0 +1,51 @@
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 TEXTUREMANAGER_H
24+#define TEXTUREMANAGER_H
25+
26+#include "texture.h"
27+#include <framework/core/declarations.h>
28+
29+class TextureManager
30+{
31+public:
32+ void init();
33+ void terminate();
34+
35+ void clearCache();
36+ void reload();
37+
38+ void preload(const std::string& fileName) { getTexture(fileName); }
39+ TexturePtr getTexture(const std::string& fileName);
40+ TexturePtr loadTexture(std::stringstream& file, const std::string& source);
41+
42+private:
43+ std::unordered_map<std::string, TexturePtr> m_textures;
44+ std::vector<AnimatedTexturePtr> m_animatedTextures;
45+ ScheduledEventPtr m_liveReloadEvent;
46+ std::list<uint> m_texturesToRelease;
47+};
48+
49+extern TextureManager g_textures;
50+
51+#endif
A RD_Client/src/framework/graphics/vertexarray.h +129-0 View file
@@ -0,0 +1,129 @@
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 VERTEXARRAY_H
24+#define VERTEXARRAY_H
25+
26+#include "declarations.h"
27+#include "hardwarebuffer.h"
28+#include <framework/util/databuffer.h>
29+
30+class VertexArray
31+{
32+ enum {
33+ CACHE_MIN_VERTICES_COUNT = 48
34+ };
35+public:
36+ VertexArray() {}
37+ ~VertexArray()
38+ {
39+ if (m_hardwareBuffer)
40+ delete m_hardwareBuffer;
41+ }
42+ VertexArray(VertexArray& c) : m_buffer(c.m_buffer)
43+ {
44+ m_hardwareBuffer = nullptr;
45+ }
46+ VertexArray& operator=(VertexArray& c) = delete;
47+
48+ inline void addVertex(float x, float y) { m_buffer << x << y; }
49+ inline void addTriangle(const Point& a, const Point& b, const Point& c) {
50+ addVertex(a.x, a.y);
51+ addVertex(b.x, b.y);
52+ addVertex(c.x, c.y);
53+ }
54+ inline void addRect(const Rect& rect)
55+ {
56+ float top = rect.top();
57+ float right = rect.right() + 1;
58+ float bottom = rect.bottom() + 1;
59+ float left = rect.left();
60+
61+ addVertex(left, top);
62+ addVertex(right, top);
63+ addVertex(left, bottom);
64+ addVertex(left, bottom);
65+ addVertex(right, top);
66+ addVertex(right, bottom);
67+ }
68+ inline void addRect(const RectF& rect)
69+ {
70+ float top = rect.top();
71+ float right = rect.right() + 1.f;
72+ float bottom = rect.bottom() + 1.f;
73+ float left = rect.left();
74+
75+ addVertex(left, top);
76+ addVertex(right, top);
77+ addVertex(left, bottom);
78+ addVertex(left, bottom);
79+ addVertex(right, top);
80+ addVertex(right, bottom);
81+ }
82+
83+ inline void addQuad(const Rect& rect) {
84+ float top = rect.top();
85+ float right = rect.right()+1;
86+ float bottom = rect.bottom()+1;
87+ float left = rect.left();
88+
89+ addVertex(left, top);
90+ addVertex(right, top);
91+ addVertex(left, bottom);
92+ addVertex(right, bottom);
93+ }
94+
95+ inline void addUpsideDownQuad(const Rect& rect) {
96+ float top = rect.top();
97+ float right = rect.right()+1;
98+ float bottom = rect.bottom()+1;
99+ float left = rect.left();
100+
101+ addVertex(left, bottom);
102+ addVertex(right, bottom);
103+ addVertex(left, top);
104+ addVertex(right, top);
105+ }
106+
107+ void clear() { m_buffer.reset(); }
108+ float *vertices() const { return m_buffer.data(); }
109+ int vertexCount() const { return m_buffer.size() / 2; }
110+ int size() const { return m_buffer.size(); }
111+
112+ // cache
113+ void cache()
114+ {
115+ if (m_buffer.size() < CACHE_MIN_VERTICES_COUNT) return;
116+ if (m_hardwareBuffer) return;
117+ m_hardwareBuffer = new HardwareBuffer(HardwareBuffer::VertexBuffer);
118+ m_hardwareBuffer->bind();
119+ m_hardwareBuffer->write((void*)m_buffer.data(), m_buffer.size() * sizeof(float), HardwareBuffer::StaticDraw);
120+ }
121+ bool isCached() { return m_hardwareBuffer != nullptr; }
122+ HardwareBuffer* getHardwareCache() { return m_hardwareBuffer; }
123+
124+private:
125+ DataBuffer<float> m_buffer;
126+ HardwareBuffer* m_hardwareBuffer = nullptr;
127+};
128+
129+#endif
A RD_Client/src/framework/http/http.cpp +225-0 View file
@@ -0,0 +1,225 @@
1+#include <framework/global.h>
2+#include <framework/luaengine/luainterface.h>
3+#include <framework/util/crypt.h>
4+#include <framework/util/stats.h>
5+#include <framework/core/eventdispatcher.h>
6+
7+#include "http.h"
8+#ifndef __EMSCRIPTEN__
9+#include "session.h"
10+#include "websocket.h"
11+#endif
12+
13+Http g_http;
14+
15+void Http::init() {
16+ m_working = true;
17+ m_thread = std::thread([&] {
18+ m_ios.run();
19+ });
20+}
21+
22+void Http::terminate() {
23+ if (!m_working)
24+ return;
25+ m_working = false;
26+#ifndef __EMSCRIPTEN__
27+ for (auto& ws : m_websockets) {
28+ ws.second->close();
29+ }
30+#endif
31+ for (auto& op : m_operations) {
32+ op.second->canceled = true;
33+ }
34+ m_guard.reset();
35+ if (!m_thread.joinable()) {
36+ stdext::millisleep(100);
37+ }
38+ m_ios.stop();
39+ m_thread.join();
40+}
41+
42+int Http::get(const std::string& url, int timeout) {
43+ if (!timeout) // lua is not working with default values
44+ timeout = 5;
45+ int operationId = m_operationId++;
46+
47+ boost::asio::post(m_ios, [&, url, timeout, operationId] {
48+ auto result = std::make_shared<HttpResult>();
49+ result->url = url;
50+ result->operationId = operationId;
51+ m_operations[operationId] = result;
52+#ifndef __EMSCRIPTEN__
53+ auto session = std::make_shared<HttpSession>(m_ios, url, m_userAgent, timeout, false, result, [&](HttpResult_ptr result) {
54+ bool finished = result->finished;
55+ g_dispatcher.addEventEx("Http::onGet", [result, finished]() {
56+ if (!finished) {
57+ g_lua.callGlobalField("g_http", "onGetProgress", result->operationId, result->url, result->progress);
58+ return;
59+ }
60+ g_lua.callGlobalField("g_http", "onGet", result->operationId, result->url, result->error, std::string(result->response.begin(), result->response.end()));
61+ });
62+ if (finished) {
63+ m_operations.erase(operationId);
64+ }
65+ });
66+ session->start();
67+#endif
68+ });
69+
70+ return operationId;
71+}
72+
73+int Http::post(const std::string& url, const std::string& data, int timeout, bool isJson) {
74+ if (!timeout) // lua is not working with default values
75+ timeout = 5;
76+ if (data.empty()) {
77+ g_logger.error(stdext::format("Invalid post request for %s, empty data, use get instead", url));
78+ return -1;
79+ }
80+
81+ int operationId = m_operationId++;
82+ boost::asio::post(m_ios, [&, url, data, timeout, isJson, operationId] {
83+ auto result = std::make_shared<HttpResult>();
84+ result->url = url;
85+ result->operationId = operationId;
86+ result->postData = data;
87+ m_operations[operationId] = result;
88+#ifndef __EMSCRIPTEN__
89+ auto session = std::make_shared<HttpSession>(m_ios, url, m_userAgent, timeout, isJson, result, [&](HttpResult_ptr result) {
90+ bool finished = result->finished;
91+ g_dispatcher.addEventEx("Http::onPost", [result, finished]() {
92+ if (!finished) {
93+ g_lua.callGlobalField("g_http", "onPostProgress", result->operationId, result->url, result->progress);
94+ return;
95+ }
96+ g_lua.callGlobalField("g_http", "onPost", result->operationId, result->url, result->error, std::string(result->response.begin(), result->response.end()));
97+ });
98+ if (finished) {
99+ m_operations.erase(operationId);
100+ }
101+ });
102+ session->start();
103+#endif
104+ });
105+ return operationId;
106+}
107+
108+int Http::download(const std::string& url, std::string path, int timeout) {
109+ if (!timeout) // lua is not working with default values
110+ timeout = 5;
111+
112+ int operationId = m_operationId++;
113+ boost::asio::post(m_ios, [&, url, path, timeout, operationId] {
114+ auto result = std::make_shared<HttpResult>();
115+ result->url = url;
116+ result->operationId = operationId;
117+ m_operations[operationId] = result;
118+#ifndef __EMSCRIPTEN__
119+ auto session = std::make_shared<HttpSession>(m_ios, url, m_userAgent, timeout, false, result, [&, path](HttpResult_ptr result) {
120+ m_speed = ((result->size) * 10) / (1 + stdext::micros() - m_lastSpeedUpdate);
121+ m_lastSpeedUpdate = stdext::micros();
122+
123+ if (!result->finished) {
124+ int speed = m_speed;
125+ g_dispatcher.addEventEx("Http::onDownloadProgress", [result, speed]() {
126+ g_lua.callGlobalField("g_http", "onDownloadProgress", result->operationId, result->url, result->progress, speed);
127+ });
128+ return;
129+ }
130+ std::string checksum = g_crypt.crc32(std::string(result->response.begin(), result->response.end()), false);
131+ g_dispatcher.addEventEx("Http::onDownload", [&, result, path, checksum]() {
132+ if (result->error.empty()) {
133+ if (!path.empty() && path[0] == '/')
134+ m_downloads[path.substr(1)] = result;
135+ else
136+ m_downloads[path] = result;
137+ }
138+ g_lua.callGlobalField("g_http", "onDownload", result->operationId, result->url, result->error, path, checksum);
139+ });
140+ m_operations.erase(operationId);
141+ });
142+ session->start();
143+#endif
144+ });
145+ return operationId;
146+}
147+
148+int Http::ws(const std::string& url, int timeout)
149+{
150+ if (!timeout) // lua is not working with default values
151+ timeout = 5;
152+ int operationId = m_operationId++;
153+
154+ boost::asio::post(m_ios, [&, url, timeout, operationId] {
155+ auto result = std::make_shared<HttpResult>();
156+ result->url = url;
157+ result->operationId = operationId;
158+ m_operations[operationId] = result;
159+#ifndef __EMSCRIPTEN__
160+ auto session = std::make_shared<WebsocketSession>(m_ios, url, m_userAgent, timeout, result, [&, result](WebsocketCallbackType type, std::string message) {
161+ g_dispatcher.addEventEx("Http::ws", [result, type, message]() {
162+ if (type == WEBSOCKET_OPEN) {
163+ g_lua.callGlobalField("g_http", "onWsOpen", result->operationId, message);
164+ } else if (type == WEBSOCKET_MESSAGE) {
165+ g_lua.callGlobalField("g_http", "onWsMessage", result->operationId, message);
166+ } else if (type == WEBSOCKET_CLOSE) {
167+ g_lua.callGlobalField("g_http", "onWsClose", result->operationId, message);
168+ } else if (type == WEBSOCKET_ERROR) {
169+ g_lua.callGlobalField("g_http", "onWsError", result->operationId, message);
170+ }
171+ });
172+ if (type == WEBSOCKET_CLOSE) {
173+ m_websockets.erase(result->operationId);
174+ }
175+ });
176+ m_websockets[result->operationId] = session;
177+ session->start();
178+#endif
179+ });
180+
181+ return operationId;
182+}
183+
184+bool Http::wsSend(int operationId, std::string message)
185+{
186+#ifndef __EMSCRIPTEN__
187+ boost::asio::post(m_ios, [&, operationId, message] {
188+ auto wit = m_websockets.find(operationId);
189+ if (wit == m_websockets.end()) {
190+ return;
191+ }
192+ wit->second->send(message);
193+ });
194+#endif
195+ return true;
196+}
197+
198+bool Http::wsClose(int operationId)
199+{
200+ cancel(operationId);
201+ return true;
202+}
203+
204+
205+bool Http::cancel(int id) {
206+#ifndef __EMSCRIPTEN__
207+ boost::asio::post(m_ios, [&, id] {
208+ auto wit = m_websockets.find(id);
209+ if (wit != m_websockets.end()) {
210+ wit->second->close();
211+ }
212+ auto it = m_operations.find(id);
213+ if (it == m_operations.end())
214+ return;
215+ if (it->second->canceled)
216+ return;
217+ it->second->canceled = true;
218+ if (auto session = it->second->session.lock()) {
219+ session->cancel();
220+ }
221+ });
222+#endif
223+ return true;
224+}
225+
A RD_Client/src/framework/http/http.h +63-0 View file
@@ -0,0 +1,63 @@
1+#ifndef HTTP_H
2+#define HTTP_H
3+
4+#include <framework/global.h>
5+#include "result.h"
6+
7+class WebsocketSession;
8+
9+class Http {
10+public:
11+ Http() : m_ios(), m_guard(boost::asio::make_work_guard(m_ios)) {}
12+
13+ void init();
14+ void terminate();
15+
16+ int get(const std::string& url, int timeout = 5);
17+ int post(const std::string& url, const std::string& data, int timeout = 5, bool isJson = false);
18+ int download(const std::string& url, std::string path, int timeout = 5);
19+ int ws(const std::string& url, int timeout = 5);
20+ bool wsSend(int operationId, std::string message);
21+ bool wsClose(int operationId);
22+
23+ bool cancel(int id);
24+
25+ const std::map<std::string, HttpResult_ptr>& downloads() {
26+ return m_downloads;
27+ }
28+ void clearDownloads() {
29+ m_downloads.clear();
30+ }
31+ HttpResult_ptr getFile(std::string path) {
32+ if (!path.empty() && path[0] == '/')
33+ path = path.substr(1);
34+ auto it = m_downloads.find(path);
35+ if (it == m_downloads.end())
36+ return nullptr;
37+ return it->second;
38+ }
39+
40+ void setUserAgent(const std::string& userAgent)
41+ {
42+ m_userAgent = userAgent;
43+ }
44+
45+private:
46+ bool m_working = false;
47+ int m_operationId = 1;
48+ int m_speed = 0;
49+ size_t m_lastSpeedUpdate = 0;
50+ std::thread m_thread;
51+ boost::asio::io_context m_ios;
52+ boost::asio::executor_work_guard<boost::asio::io_context::executor_type> m_guard;
53+ std::map<int, HttpResult_ptr> m_operations;
54+#ifndef __EMSCRIPTEN__
55+ std::map<int, std::shared_ptr<WebsocketSession>> m_websockets;
56+#endif
57+ std::map<std::string, HttpResult_ptr> m_downloads;
58+ std::string m_userAgent = "Mozilla/5.0";
59+};
60+
61+extern Http g_http;
62+
63+#endif // ! HTTP_H
A RD_Client/src/framework/http/result.h +28-0 View file
@@ -0,0 +1,28 @@
1+#pragma once
2+
3+#include <string>
4+#include <memory>
5+#include <functional>
6+#include <vector>
7+
8+class HttpSession;
9+
10+struct HttpResult {
11+ std::string url;
12+ int operationId = 0;
13+ int status = 0;
14+ int size = 0;
15+ int progress = 0; // from 0 to 100
16+ int redirects = 0; // redirect
17+ bool connected = false;
18+ bool finished = false;
19+ bool canceled = false;
20+ std::string postData;
21+ std::vector<uint8_t> response;
22+ std::string error;
23+ std::weak_ptr<HttpSession> session;
24+};
25+
26+
27+using HttpResult_ptr = std::shared_ptr<HttpResult>;
28+using HttpResult_cb = std::function<void(HttpResult_ptr)>;
A RD_Client/src/framework/http/session.cpp +231-0 View file
@@ -0,0 +1,231 @@
1+#ifndef __EMSCRIPTEN__
2+
3+#include <framework/stdext/uri.h>
4+#include <chrono>
5+
6+#include "session.h"
7+#include <string_view>
8+
9+void HttpSession::start() {
10+ if (m_result->redirects >= 10) {
11+ auto self(shared_from_this());
12+ boost::asio::post(m_service, [self] {
13+ self->onError("Too many redirects");
14+ });
15+ return;
16+ }
17+ auto parsedUrl = parseURI(m_url);
18+ if (parsedUrl.domain.empty()) {
19+ auto self(shared_from_this());
20+ boost::asio::post(m_service, [self] {
21+ self->onError("Invalid url", self->m_url);
22+ });
23+ return;
24+ }
25+
26+ m_domain = parsedUrl.domain;
27+ try {
28+ m_port = parsedUrl.port.empty() ? 0 : std::stoi(parsedUrl.port);
29+ } catch (std::exception&) {
30+ }
31+ if (!m_port) {
32+ m_port = parsedUrl.protocol == "https" ? 443 : 80;
33+ }
34+
35+ m_timer.expires_after(std::chrono::seconds(m_timeout));
36+ m_timer.async_wait(std::bind(&HttpSession::onTimeout, shared_from_this(), std::placeholders::_1));
37+
38+ m_request.version(11);
39+ m_request.method(boost::beast::http::verb::get);
40+ m_request.keep_alive(false);
41+ m_request.target(parsedUrl.query);
42+ m_request.set(boost::beast::http::field::host, parsedUrl.domain);
43+ m_request.set(boost::beast::http::field::user_agent, m_agent);
44+ if (!m_result->postData.empty()) {
45+ m_request.method(boost::beast::http::verb::post);
46+ m_request.body().assign(m_result->postData);
47+ m_request.content_length(m_result->postData.size());
48+ if (m_isJson)
49+ m_request.set(boost::beast::http::field::content_type, "application/json");
50+ }
51+
52+ m_result->session = weak_from_this();
53+ m_resolver.async_resolve(m_domain, std::to_string(m_port), std::bind(&HttpSession::on_resolve, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
54+}
55+
56+void HttpSession::on_resolve(const boost::system::error_code& ec, boost::asio::ip::tcp::resolver::iterator iterator) {
57+ if (ec)
58+ return onError("resolve error", ec.message());
59+ iterator->endpoint().port(m_port);
60+ m_socket.async_connect(*iterator, std::bind(&HttpSession::on_connect, shared_from_this(), std::placeholders::_1));
61+}
62+
63+void HttpSession::on_connect(const boost::system::error_code& ec) {
64+ if (ec)
65+ return onError("connection error", ec.message());
66+
67+ if (m_url.find("https") == 0 || m_url.find("HTTPS") == 0)
68+ {
69+ //m_context.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::tlsv12_client);
70+ m_context = std::make_shared< boost::asio::ssl::context >(boost::asio::ssl::context::tlsv12_client);
71+ m_ssl = std::make_shared<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&>>(m_socket, *m_context);
72+ m_ssl->set_verify_mode(boost::asio::ssl::verify_peer);
73+ m_ssl->set_verify_callback([](bool, boost::asio::ssl::verify_context&) { return true; });
74+
75+ if(!SSL_set_tlsext_host_name(m_ssl->native_handle(), m_domain.c_str()))
76+ {
77+ boost::beast::error_code ec2(static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category());
78+ return onError("HTTPS error", ec2.message());
79+ }
80+
81+ auto self(shared_from_this());
82+ m_ssl->async_handshake(boost::asio::ssl::stream_base::client, [&, self] (const boost::system::error_code& ec) {
83+ if (ec)
84+ return onError("HTTPS handshake error", ec.message());
85+
86+ boost::beast::http::async_write(*m_ssl, m_request,
87+ std::bind(&HttpSession::on_request_sent, shared_from_this(), std::placeholders::_1));
88+ });
89+ }
90+ else
91+ {
92+ boost::beast::http::async_write(m_socket, m_request,
93+ std::bind(&HttpSession::on_request_sent, shared_from_this(), std::placeholders::_1));
94+ }
95+}
96+
97+void HttpSession::on_request_sent(const boost::system::error_code& ec) {
98+ if (ec)
99+ return onError("request sending error", ec.message());
100+ if(m_result->canceled)
101+ return onError("canceled");
102+
103+ m_response.body_limit(512 * 1024 * 1024);
104+ m_response.header_limit(4 * 1024 * 1024);
105+
106+ if (m_ssl) {
107+ boost::beast::http::async_read_header(*m_ssl, m_streambuf, m_response,
108+ std::bind(&HttpSession::on_read_header, shared_from_this(),
109+ std::placeholders::_1, std::placeholders::_2));
110+ } else {
111+ boost::beast::http::async_read_header(m_socket, m_streambuf, m_response,
112+ std::bind(&HttpSession::on_read_header, shared_from_this(),
113+ std::placeholders::_1, std::placeholders::_2));
114+ }
115+}
116+
117+void HttpSession::on_read_header(const boost::system::error_code& ec, size_t bytes_transferred) {
118+ if (ec)
119+ return onError("read header error", ec.message());
120+ if(m_result->canceled)
121+ return onError("canceled", ec.message());
122+
123+ auto msg = m_response.get();
124+ m_result->status = msg.result_int();
125+ m_result->size = atoi(std::string(msg["Content-Length"]).c_str());
126+ auto location = msg["Location"];
127+
128+ if (!location.empty()) {
129+ auto session = std::make_shared<HttpSession>(m_service, std::string(location), m_agent, m_timeout, m_isJson, m_result, m_callback);
130+ session->start();
131+ return close();
132+ }
133+
134+ if (msg.result_int() < 200 || msg.result_int() >= 300)
135+ return onError("Invalid http status code", std::to_string(msg.result_int()));
136+
137+ if (m_response.is_done()) { // there's nothing more to read
138+ return on_read(ec, 0);
139+ }
140+
141+ if (m_ssl) {
142+ boost::beast::http::async_read_some(*m_ssl, m_streambuf, m_response,
143+ std::bind(&HttpSession::on_read, shared_from_this(),
144+ std::placeholders::_1, std::placeholders::_2));
145+ } else {
146+ boost::beast::http::async_read_some(m_socket, m_streambuf, m_response,
147+ std::bind(&HttpSession::on_read, shared_from_this(),
148+ std::placeholders::_1, std::placeholders::_2));
149+ }
150+}
151+
152+void HttpSession::on_read(const boost::system::error_code& ec, size_t bytes_transferred) {
153+ if(m_result->canceled)
154+ return onError("canceled", ec.message());
155+ if (ec && ec != boost::beast::http::error::end_of_stream)
156+ return onError("read error", ec.message());
157+ else if (ec == boost::beast::http::error::end_of_stream || m_response.is_done()) {
158+ if (!m_result->finished) {
159+ m_result->finished = true;
160+ m_result->progress = 100;
161+ auto buffer = m_response.get().body();
162+ m_result->response.reserve(buffer.size());
163+ auto buffers = buffer.data();
164+ for (auto b : buffers) {
165+ m_result->response.insert(m_result->response.end(), static_cast<const uint8_t*>(b.data()), static_cast<const uint8_t*>(b.data()) + b.size());
166+ }
167+ m_callback(m_result);
168+ }
169+ return close();
170+ }
171+
172+ if (m_result->size > 0) {
173+ //m_callback
174+ int new_progress = (int)std::min<int64_t>(100ll, (100ll * (int64_t)m_response.get().payload_size().get_value_or(0)) / (int64_t)m_result->size);
175+ if (!m_result->finished && new_progress != m_result->progress) // update progress
176+ m_callback(m_result);
177+ m_result->progress = new_progress;
178+ }
179+ m_timer.expires_after(std::chrono::seconds(m_timeout));
180+
181+ if (m_ssl) {
182+ boost::beast::http::async_read_some(*m_ssl, m_streambuf, m_response,
183+ std::bind(&HttpSession::on_read, shared_from_this(),
184+ std::placeholders::_1, std::placeholders::_2));
185+ } else {
186+ boost::beast::http::async_read_some(m_socket, m_streambuf, m_response,
187+ std::bind(&HttpSession::on_read, shared_from_this(),
188+ std::placeholders::_1, std::placeholders::_2));
189+ }
190+}
191+
192+void HttpSession::close() {
193+ m_timer.cancel();
194+ if (m_ssl) {
195+ auto self(shared_from_this());
196+ m_ssl->async_shutdown([&, self](const boost::system::error_code& error) {
197+ boost::system::error_code ec;
198+ m_socket.close(ec);
199+ });
200+ } else {
201+ boost::system::error_code ec;
202+ m_socket.close(ec);
203+ }
204+}
205+
206+
207+void HttpSession::onTimeout(const boost::system::error_code& error)
208+{
209+ if(error)
210+ return;
211+
212+ return onError("timeout");
213+}
214+
215+void HttpSession::onError(const std::string& error, const std::string& details) {
216+ boost::system::error_code ec;
217+ m_socket.close(ec);
218+ m_timer.cancel(ec);
219+ if (!m_result->finished) {
220+ m_result->finished = true;
221+ m_result->error = error;
222+ if (!details.empty()) {
223+ m_result->error += " (";
224+ m_result->error += details;
225+ m_result->error += ")";
226+ }
227+ m_callback(m_result);
228+ }
229+}
230+
231+#endif
A RD_Client/src/framework/http/session.h +58-0 View file
@@ -0,0 +1,58 @@
1+#pragma once
2+
3+#include <framework/global.h>
4+
5+#include <iostream>
6+#include <string>
7+#include <memory>
8+#include <functional>
9+#include <future>
10+
11+#include "result.h"
12+
13+class HttpSession : public std::enable_shared_from_this<HttpSession>
14+{
15+public:
16+
17+ HttpSession(boost::asio::io_service& service, const std::string& url, const std::string& agent,
18+ int timeout, bool isJson, HttpResult_ptr result, HttpResult_cb callback) :
19+ m_service(service), m_url(url), m_agent(agent), m_socket(service), m_resolver(service),
20+ m_callback(callback), m_result(result), m_timer(service), m_timeout(timeout), m_isJson(isJson)
21+ {
22+ VALIDATE(m_callback);
23+ VALIDATE(m_result);
24+ };
25+
26+ void start();
27+ void cancel() { onError("canceled"); }
28+
29+private:
30+ boost::asio::io_service& m_service;
31+ std::string m_url;
32+ std::string m_agent;
33+ int m_port;
34+ boost::asio::ip::tcp::socket m_socket;
35+ boost::asio::ip::tcp::resolver m_resolver;
36+ HttpResult_cb m_callback;
37+ HttpResult_ptr m_result;
38+ boost::asio::steady_timer m_timer;
39+ int m_timeout;
40+ bool m_isJson = false;
41+
42+ std::string m_domain;
43+ std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket&>> m_ssl;
44+ std::shared_ptr<boost::asio::ssl::context> m_context;
45+
46+ boost::beast::flat_buffer m_streambuf{ 512 * 1024 * 1024 }; // limited to 512MB
47+ boost::beast::http::request<boost::beast::http::string_body> m_request;
48+ boost::beast::http::response_parser<boost::beast::http::dynamic_body> m_response;
49+
50+ void on_resolve(const boost::system::error_code& ec, boost::asio::ip::tcp::resolver::iterator iterator);
51+ void on_connect(const boost::system::error_code& ec);
52+ void on_request_sent(const boost::system::error_code& ec);
53+ void on_read_header(const boost::system::error_code & ec, size_t bytes_transferred);
54+ void on_read(const boost::system::error_code& ec, size_t bytes_transferred);
55+ void close();
56+ void onTimeout(const boost::system::error_code& error);
57+ void onError(const std::string& error, const std::string& details = "");
58+};
A RD_Client/src/framework/http/websocket.cpp +202-0 View file
@@ -0,0 +1,202 @@
1+#ifndef __EMSCRIPTEN__
2+
3+#include <framework/stdext/uri.h>
4+#include <chrono>
5+
6+#include "websocket.h"
7+
8+void WebsocketSession::start() {
9+ if (m_result->redirects >= 10) {
10+ auto self(shared_from_this());
11+ boost::asio::post(m_service, [self] {
12+ self->onError("Too many redirects");
13+ });
14+ return;
15+ }
16+ auto parsedUrl = parseURI(m_url);
17+ if (parsedUrl.domain.empty()) {
18+ auto self(shared_from_this());
19+ boost::asio::post(m_service, [self] {
20+ self->onError("Invalid url", self->m_url);
21+ });
22+ return;
23+ }
24+
25+ m_domain = parsedUrl.domain;
26+ try {
27+ m_port = parsedUrl.port.empty() ? 0 : std::stoi(parsedUrl.port);
28+ } catch (std::exception&) {
29+ }
30+ if (!m_port) {
31+ m_port = parsedUrl.protocol == "wss" ? 443 : 80;
32+ }
33+
34+ m_closed = false;
35+ m_timer.expires_after(std::chrono::seconds(m_timeout));
36+ m_timer.async_wait(std::bind(&WebsocketSession::onTimeout, shared_from_this(), std::placeholders::_1));
37+
38+ if (m_url.find("wss") == 0 || m_url.find("WSS") == 0) {
39+ m_context = std::make_shared< boost::asio::ssl::context >(boost::asio::ssl::context::tlsv12_client);
40+ m_ssl = std::make_shared<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::tcp_stream>>>(m_service, *m_context);
41+ m_ssl->next_layer().set_verify_mode(boost::asio::ssl::verify_peer);
42+ m_ssl->next_layer().set_verify_callback([](bool, boost::asio::ssl::verify_context&) { return true; });
43+ if (!SSL_set_tlsext_host_name(m_ssl->next_layer().native_handle(), m_domain.c_str())) {
44+ boost::beast::error_code ec2(static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category());
45+ return onError("WSS error", ec2.message());
46+ }
47+ } else {
48+ m_socket = std::make_shared<boost::beast::websocket::stream<boost::beast::tcp_stream>>(m_service);
49+ }
50+
51+ m_resolver.async_resolve(m_domain, std::to_string(m_port), std::bind(&WebsocketSession::on_resolve, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
52+}
53+
54+void WebsocketSession::send(std::string data)
55+{
56+ if (m_closed)
57+ return;
58+
59+ bool sendNow = m_result->connected && m_sendQueue.empty();
60+ m_sendQueue.push(data);
61+ if (sendNow) {
62+ if (m_ssl) {
63+ m_ssl->async_write(boost::asio::buffer(m_sendQueue.front(), m_sendQueue.front().size()), std::bind(&WebsocketSession::on_send, shared_from_this(), std::placeholders::_1));
64+ } else {
65+ m_socket->async_write(boost::asio::buffer(m_sendQueue.front(), m_sendQueue.front().size()), std::bind(&WebsocketSession::on_send, shared_from_this(), std::placeholders::_1));
66+ }
67+ }
68+}
69+
70+
71+void WebsocketSession::on_resolve(const boost::system::error_code& ec, boost::asio::ip::tcp::resolver::iterator iterator) {
72+ if (ec)
73+ return onError("resolve error", ec.message());
74+ iterator->endpoint().port(m_port);
75+ if (m_ssl) {
76+ boost::beast::get_lowest_layer(*m_ssl).async_connect(*iterator, std::bind(&WebsocketSession::on_connect, shared_from_this(), std::placeholders::_1));
77+ } else {
78+ boost::beast::get_lowest_layer(*m_socket).async_connect(*iterator, std::bind(&WebsocketSession::on_connect, shared_from_this(), std::placeholders::_1));
79+ }
80+}
81+
82+void WebsocketSession::on_connect(const boost::system::error_code& ec) {
83+ if (ec)
84+ return onError("connection error", ec.message());
85+
86+ if (m_url.find("wss") == 0 || m_url.find("WSS") == 0) {
87+ //m_context.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::tlsv12_client);
88+
89+ auto self(shared_from_this());
90+ m_ssl->next_layer().async_handshake(boost::asio::ssl::stream_base::client, [&, self](const boost::system::error_code& ec) {
91+ if (ec)
92+ return onError("WSS handshake error", ec.message());
93+
94+ auto parsedUrl = parseURI(m_url);
95+ m_ssl->async_handshake(m_domain, parsedUrl.query, std::bind(&WebsocketSession::on_handshake, shared_from_this(), std::placeholders::_1));
96+ });
97+ return;
98+ }
99+
100+ auto parsedUrl = parseURI(m_url);
101+ m_socket->async_handshake(m_domain, parsedUrl.query, std::bind(&WebsocketSession::on_handshake, shared_from_this(), std::placeholders::_1));
102+}
103+
104+void WebsocketSession::on_handshake(const boost::system::error_code& ec)
105+{
106+ if (ec)
107+ return onError("handshake error", ec.message());
108+ if (m_closed)
109+ return;
110+
111+ m_result->connected = true;
112+ m_callback(WEBSOCKET_OPEN, "");
113+ if (m_ssl) {
114+ m_ssl->async_read(m_streambuf, std::bind(&WebsocketSession::on_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
115+ if (!m_sendQueue.empty()) {
116+ m_ssl->async_write(boost::asio::buffer(m_sendQueue.front(), m_sendQueue.front().size()), std::bind(&WebsocketSession::on_send, shared_from_this(), std::placeholders::_1));
117+ }
118+ } else {
119+ m_socket->async_read(m_streambuf, std::bind(&WebsocketSession::on_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
120+ if (!m_sendQueue.empty()) {
121+ m_socket->async_write(boost::asio::buffer(m_sendQueue.front(), m_sendQueue.front().size()), std::bind(&WebsocketSession::on_send, shared_from_this(), std::placeholders::_1));
122+ }
123+ }
124+}
125+
126+void WebsocketSession::on_send(const boost::system::error_code& ec) {
127+ if(ec)
128+ return onError("send error", ec.message());
129+ m_sendQueue.pop();
130+ if (m_closed)
131+ return;
132+
133+ if (!m_sendQueue.empty()) {
134+ if (m_ssl) {
135+ m_ssl->async_write(boost::asio::buffer(m_sendQueue.front(), m_sendQueue.front().size()), std::bind(&WebsocketSession::on_send, shared_from_this(), std::placeholders::_1));
136+ } else {
137+ m_socket->async_write(boost::asio::buffer(m_sendQueue.front(), m_sendQueue.front().size()), std::bind(&WebsocketSession::on_send, shared_from_this(), std::placeholders::_1));
138+ }
139+ }
140+}
141+
142+void WebsocketSession::on_read(const boost::system::error_code& ec, size_t bytes_transferred) {
143+ if(m_result->canceled)
144+ return onError("canceled", ec.message());
145+ if (ec)
146+ return onError("read error", ec.message());
147+ if (m_closed)
148+ return;
149+
150+ m_timer.expires_after(std::chrono::seconds(m_timeout));
151+ m_timer.async_wait(std::bind(&WebsocketSession::onTimeout, shared_from_this(), std::placeholders::_1));
152+
153+ m_callback(WEBSOCKET_MESSAGE, boost::beast::buffers_to_string(m_streambuf.data()));
154+ m_streambuf.clear();
155+
156+ if (m_ssl) {
157+ m_ssl->async_read(m_streambuf, std::bind(&WebsocketSession::on_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
158+ } else {
159+ m_socket->async_read(m_streambuf, std::bind(&WebsocketSession::on_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
160+ }
161+}
162+
163+void WebsocketSession::close() {
164+ m_timer.cancel();
165+ if (!m_closed) {
166+ m_closed = true;
167+ m_callback(WEBSOCKET_CLOSE, "");
168+ }
169+ boost::system::error_code ec;
170+ if (m_ssl) {
171+ m_ssl->close(boost::beast::websocket::close_reason(""), ec);
172+ } else if(m_socket) {
173+ m_socket->close(boost::beast::websocket::close_reason(""), ec);
174+ }
175+}
176+
177+void WebsocketSession::onTimeout(const boost::system::error_code& error)
178+{
179+ if(error)
180+ return;
181+
182+ return onError("timeout");
183+}
184+
185+void WebsocketSession::onError(const std::string& error, const std::string& details) {
186+ m_result->connected = false;
187+ if (!m_result->finished) {
188+ m_result->finished = true;
189+ std::string msg = error;
190+ if (!details.empty()) {
191+ msg += " (";
192+ msg += details;
193+ msg += ")";
194+ }
195+ if (!m_closed) {
196+ m_callback(WEBSOCKET_ERROR, msg);
197+ }
198+ }
199+ close();
200+}
201+
202+#endif
A RD_Client/src/framework/http/websocket.h +66-0 View file
@@ -0,0 +1,66 @@
1+#pragma once
2+
3+#include <framework/global.h>
4+
5+#include <iostream>
6+#include <string>
7+#include <memory>
8+#include <functional>
9+#include <future>
10+#include <queue>
11+#include <vector>
12+
13+#include "result.h"
14+
15+enum WebsocketCallbackType {
16+ WEBSOCKET_OPEN,
17+ WEBSOCKET_MESSAGE,
18+ WEBSOCKET_ERROR,
19+ WEBSOCKET_CLOSE
20+};
21+
22+using WebsocketSession_cb = std::function<void(WebsocketCallbackType, std::string message)>;
23+
24+class WebsocketSession : public std::enable_shared_from_this<WebsocketSession>
25+{
26+public:
27+
28+ WebsocketSession(boost::asio::io_service& service, const std::string& url, const std::string& agent, int timeout, HttpResult_ptr result, WebsocketSession_cb callback) :
29+ m_service(service), m_url(url), m_agent(agent), m_resolver(service), m_callback(callback), m_result(result), m_timer(service), m_timeout(timeout)
30+ {
31+ VALIDATE(m_callback);
32+ VALIDATE(m_result);
33+ };
34+
35+ void start();
36+ void send(std::string data);
37+ void close();
38+
39+private:
40+ boost::asio::io_service& m_service;
41+ std::string m_url;
42+ std::string m_agent;
43+ boost::asio::ip::tcp::resolver m_resolver;
44+ WebsocketSession_cb m_callback;
45+ HttpResult_ptr m_result;
46+ boost::asio::steady_timer m_timer;
47+ int m_timeout;
48+ bool m_closed;
49+ std::string m_domain;
50+ int m_port;
51+
52+ std::shared_ptr<boost::beast::websocket::stream<boost::beast::tcp_stream>> m_socket;
53+ std::shared_ptr<boost::beast::websocket::stream<boost::beast::ssl_stream<boost::beast::tcp_stream>>> m_ssl;
54+ std::shared_ptr<boost::asio::ssl::context> m_context;
55+
56+ boost::beast::flat_buffer m_streambuf{ 16 * 1024 * 1024 }; // limited to 16MB
57+ std::queue<std::string> m_sendQueue;
58+
59+ void on_resolve(const boost::system::error_code& ec, boost::asio::ip::tcp::resolver::iterator iterator);
60+ void on_connect(const boost::system::error_code& ec);
61+ void on_handshake(const boost::system::error_code& ec);
62+ void on_send(const boost::system::error_code& ec);
63+ void on_read(const boost::system::error_code& ec, size_t bytes_transferred);
64+ void onTimeout(const boost::system::error_code& error);
65+ void onError(const std::string& error, const std::string& details = "");
66+};
A RD_Client/src/framework/input/mouse.cpp +129-0 View file
@@ -0,0 +1,129 @@
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 "mouse.h"
24+#include <framework/ui/uiwidget.h>
25+#include <framework/platform/platformwindow.h>
26+#include <framework/core/eventdispatcher.h>
27+#include <framework/core/resourcemanager.h>
28+
29+Mouse g_mouse;
30+
31+void Mouse::init()
32+{
33+}
34+
35+void Mouse::terminate()
36+{
37+ m_cursors.clear();
38+}
39+
40+void Mouse::loadCursors(std::string filename)
41+{
42+ filename = g_resources.guessFilePath(filename, "otml");
43+ try {
44+ OTMLDocumentPtr doc = OTMLDocument::parse(filename);
45+ OTMLNodePtr cursorsNode = doc->at("Cursors");
46+
47+ for(const OTMLNodePtr& cursorNode : cursorsNode->children())
48+ addCursor(cursorNode->tag(),
49+ stdext::resolve_path(cursorNode->valueAt("image"), cursorNode->source()),
50+ cursorNode->valueAt<Point>("hot-spot"));
51+ } catch(stdext::exception& e) {
52+ g_logger.error(stdext::format("unable to load cursors file: %s", e.what()));
53+ }
54+}
55+
56+void Mouse::addCursor(const std::string& name, const std::string& file, const Point& hotSpot)
57+{
58+ if (g_graphicsThreadId != std::this_thread::get_id()) {
59+ g_graphicsDispatcher.addEvent(std::bind(&Mouse::addCursor, this, name, file, hotSpot));
60+ return;
61+ }
62+
63+ int cursorId = g_window.loadMouseCursor(file, hotSpot);
64+ if(cursorId >= 0) {
65+ m_cursors[name] = cursorId;
66+ } else
67+ g_logger.error(stdext::format("unable to load cursor %s", name));
68+}
69+
70+void Mouse::pushCursor(const std::string& name)
71+{
72+ if (g_graphicsThreadId != std::this_thread::get_id()) {
73+ g_graphicsDispatcher.addEvent(std::bind(&Mouse::pushCursor, this, name));
74+ return;
75+ }
76+
77+ auto it = m_cursors.find(name);
78+ if(it == m_cursors.end())
79+ return;
80+
81+ int cursorId = it->second;
82+ g_window.setMouseCursor(cursorId);
83+ std::lock_guard<std::mutex> lock(m_mutex);
84+ m_cursorStack.push_back(cursorId);
85+ return;
86+}
87+
88+void Mouse::popCursor(const std::string& name)
89+{
90+ if (g_graphicsThreadId != std::this_thread::get_id()) {
91+ g_graphicsDispatcher.addEvent(std::bind(&Mouse::popCursor, this, name));
92+ return;
93+ }
94+
95+ std::lock_guard<std::mutex> lock(m_mutex);
96+ if(m_cursorStack.size() == 0)
97+ return;
98+
99+ if(name.empty() || m_cursors.find(name) == m_cursors.end())
100+ m_cursorStack.pop_back();
101+ else {
102+ int cursorId = m_cursors[name];
103+ int index = -1;
104+ for(uint i=0;i<m_cursorStack.size();++i) {
105+ if(m_cursorStack[i] == cursorId)
106+ index = i;
107+ }
108+ if(index >= 0)
109+ m_cursorStack.erase(m_cursorStack.begin() + index);
110+ else
111+ return;
112+ }
113+
114+ if(m_cursorStack.size() > 0)
115+ g_window.setMouseCursor(m_cursorStack.back());
116+ else
117+ g_window.restoreMouseCursor();
118+}
119+
120+bool Mouse::isCursorChanged()
121+{
122+ std::lock_guard<std::mutex> lock(m_mutex);
123+ return m_cursorStack.size() > 0;
124+}
125+
126+bool Mouse::isPressed(Fw::MouseButton mouseButton)
127+{
128+ return g_window.isMouseButtonPressed(mouseButton);
129+}
A RD_Client/src/framework/input/mouse.h +44-0 View file
@@ -0,0 +1,44 @@
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/global.h>
24+
25+class Mouse
26+{
27+public:
28+ void init();
29+ void terminate();
30+
31+ void loadCursors(std::string filename);
32+ void addCursor(const std::string& name, const std::string& file, const Point& hotSpot);
33+ void pushCursor(const std::string& name);
34+ void popCursor(const std::string& name);
35+ bool isCursorChanged();
36+ bool isPressed(Fw::MouseButton mouseButton);
37+
38+private:
39+ std::map<std::string, int> m_cursors;
40+ std::deque<int> m_cursorStack;
41+ std::mutex m_mutex;
42+};
43+
44+extern Mouse g_mouse;
A RD_Client/src/framework/luaengine/declarations.h +37-0 View file
@@ -0,0 +1,37 @@
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_LUA_DECLARATIONS_H
24+#define FRAMEWORK_LUA_DECLARATIONS_H
25+
26+#include <framework/global.h>
27+
28+#include <memory>
29+
30+class LuaInterface;
31+class LuaObject;
32+
33+typedef std::function<int(LuaInterface*)> LuaCppFunction;
34+typedef std::unique_ptr<LuaCppFunction> LuaCppFunctionPtr;
35+typedef stdext::shared_object_ptr<LuaObject> LuaObjectPtr;
36+
37+#endif
A RD_Client/src/framework/luaengine/lbitlib.cpp +379-0 View file
@@ -0,0 +1,379 @@
1+/*
2+ * This is the bit32 library from lua 5.2.0, backported to
3+ * lua 5.1.4.
4+ *
5+ * version 5.2.0-backport4
6+ *
7+ * This backport was assembled by Sean Bolton (sean at smbolton
8+ * dot com) almost entirely from the above mentioned Lua distributions,
9+ * which are:
10+ *
11+ * Copyright (C) 1994-2011 Lua.org, PUC-Rio. All rights reserved.
12+ *
13+ * Permission is hereby granted, free of charge, to any person obtaining
14+ * a copy of this software and associated documentation files (the
15+ * "Software"), to deal in the Software without restriction, including
16+ * without limitation the rights to use, copy, modify, merge, publish,
17+ * distribute, sublicense, and/or sell copies of the Software, and to
18+ * permit persons to whom the Software is furnished to do so, subject to
19+ * the following conditions:
20+ *
21+ * The above copyright notice and this permission notice shall be
22+ * included in all copies or substantial portions of the Software.
23+ *
24+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
27+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
28+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
29+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
30+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31+ */
32+
33+#define LUA_LIB
34+
35+extern "C" {
36+#if defined(_MSC_VER) || defined(ANDROID)
37+#include <luajit/lua.h>
38+#include <luajit/lualib.h>
39+#include <luajit/lauxlib.h>
40+#else
41+#include <lua.h>
42+#include <lualib.h>
43+#include <lauxlib.h>
44+#endif
45+}
46+
47+/* ----- adapted from lua-5.2.0 luaconf.h: ----- */
48+
49+/*
50+@@ LUA_UNSIGNED is the integral type used by lua_pushunsigned/lua_tounsigned.
51+** It must have at least 32 bits.
52+*/
53+#ifndef LUAI_INT32
54+#define LUAI_INT32 int
55+#endif
56+#define LUA_UNSIGNED unsigned LUAI_INT32
57+
58+#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) /* { */
59+
60+/* On a Microsoft compiler on a Pentium, use assembler to avoid clashes
61+ with a DirectX idiosyncrasy */
62+#if defined(LUA_WIN) && defined(_MSC_VER) && defined(_M_IX86) /* { */
63+
64+#define MS_ASMTRICK
65+
66+#else /* }{ */
67+/* the next definition uses a trick that should work on any machine
68+ using IEEE754 with a 32-bit integer type */
69+
70+#define LUA_IEEE754TRICK
71+
72+/*
73+@@ LUA_IEEEENDIAN is the endianness of doubles in your machine
74+** (0 for little endian, 1 for big endian); if not defined, Lua will
75+** check it dynamically.
76+*/
77+/* check for known architectures */
78+#if defined(__i386__) || defined(__i386) || defined(__X86__) || \
79+ defined (__x86_64)
80+#define LUA_IEEEENDIAN 0
81+#elif defined(__POWERPC__) || defined(__ppc__)
82+#define LUA_IEEEENDIAN 1
83+#endif
84+
85+#endif /* } */
86+
87+#endif /* } */
88+
89+/* ----- from lua-5.2.0 lua.h: ----- */
90+
91+/* unsigned integer type */
92+typedef LUA_UNSIGNED lua_Unsigned;
93+
94+/* ----- adapted from lua-5.2.0 llimits.h: ----- */
95+
96+/* lua_number2unsigned is a macro to convert a lua_Number to a lua_Unsigned.
97+** lua_unsigned2number is a macro to convert a lua_Unsigned to a lua_Number.
98+*/
99+
100+#if defined(MS_ASMTRICK) /* { */
101+/* trick with Microsoft assembler for X86 */
102+
103+#define lua_number2unsigned(i,n) \
104+ {__int64 l; __asm {__asm fld n __asm fistp l} i = (unsigned int)l;}
105+
106+#elif defined(LUA_IEEE754TRICK) /* }{ */
107+/* the next trick should work on any machine using IEEE754 with
108+ a 32-bit integer type */
109+
110+union luai_Cast2 { double l_d; LUAI_INT32 l_p[2]; };
111+
112+#if !defined(LUA_IEEEENDIAN) /* { */
113+#define LUAI_EXTRAIEEE \
114+ static const union luai_Cast2 ieeeendian = {-(33.0 + 6755399441055744.0)};
115+#define LUA_IEEEENDIAN (ieeeendian.l_p[1] == 33)
116+#else
117+#define LUAI_EXTRAIEEE /* empty */
118+#endif /* } */
119+
120+#define lua_number2int32(i,n,t) \
121+ { LUAI_EXTRAIEEE \
122+ volatile union luai_Cast2 u; u.l_d = (n) + 6755399441055744.0; \
123+ (i) = (t)u.l_p[LUA_IEEEENDIAN]; }
124+
125+#define lua_number2unsigned(i,n) lua_number2int32(i, n, lua_Unsigned)
126+
127+#endif /* } */
128+
129+#if !defined(lua_number2unsigned) /* { */
130+/* the following definition assures proper modulo behavior */
131+#if defined(LUA_NUMBER_DOUBLE)
132+#include <math.h>
133+#define SUPUNSIGNED ((lua_Number)(~(lua_Unsigned)0) + 1)
134+#define lua_number2unsigned(i,n) \
135+ ((i)=(lua_Unsigned)((n) - floor((n)/SUPUNSIGNED)*SUPUNSIGNED))
136+#else
137+#define lua_number2unsigned(i,n) ((i)=(lua_Unsigned)(n))
138+#endif
139+#endif /* } */
140+
141+/* on several machines, coercion from unsigned to double is slow,
142+ so it may be worth to avoid */
143+#define lua_unsigned2number(u) \
144+ (((u) <= (lua_Unsigned)INT_MAX) ? (lua_Number)(int)(u) : (lua_Number)(u))
145+
146+/* ----- adapted from lua-5.2.0 lapi.c: ----- */
147+
148+static void lua_pushunsigned (lua_State *L, lua_Unsigned u) {
149+ lua_Number n;
150+ n = lua_unsigned2number(u);
151+ lua_pushnumber(L, n);
152+}
153+
154+/* ----- adapted from lua-5.2.0-work3 lbitlib.c getuintarg(): ----- */
155+
156+static lua_Unsigned luaL_checkunsigned (lua_State *L, int arg) {
157+ lua_Unsigned r;
158+ lua_Number x = lua_tonumber(L, arg);
159+ if (x == 0) luaL_checktype(L, arg, LUA_TNUMBER);
160+ lua_number2unsigned(r, x);
161+ return r;
162+}
163+
164+/* ----- Lua 5.2 luaL_newlib() compatibility: ----- */
165+
166+#define LUAMOD_API LUALIB_API
167+#define LUA_BIT32LIBNAME "bit32"
168+#define luaL_newlib(x, y) luaL_register(x, LUA_BIT32LIBNAME, y)
169+
170+/* ----- avoid a 'symbol redefined' warning below ----- */
171+
172+#undef LUA_LIB
173+
174+/* ----- here follows the unmodified lbitlib.c from Lua 5.2.0 ----- */
175+
176+/*
177+** $Id: lbitlib.c,v 1.16 2011/06/20 16:35:23 roberto Exp $
178+** Standard library for bitwise operations
179+** See Copyright Notice in lua.h
180+*/
181+
182+#define lbitlib_c
183+#define LUA_LIB
184+
185+
186+/* number of bits to consider in a number */
187+#if !defined(LUA_NBITS)
188+#define LUA_NBITS 32
189+#endif
190+
191+
192+#define ALLONES (~(((~(lua_Unsigned)0) << (LUA_NBITS - 1)) << 1))
193+
194+/* macro to trim extra bits */
195+#define trim(x) ((x) & ALLONES)
196+
197+
198+/* builds a number with 'n' ones (1 <= n <= LUA_NBITS) */
199+#define mask(n) (~((ALLONES << 1) << ((n) - 1)))
200+
201+
202+typedef lua_Unsigned b_uint;
203+
204+
205+
206+static b_uint andaux (lua_State *L) {
207+ int i, n = lua_gettop(L);
208+ b_uint r = ~(b_uint)0;
209+ for (i = 1; i <= n; i++)
210+ r &= luaL_checkunsigned(L, i);
211+ return trim(r);
212+}
213+
214+
215+static int b_and (lua_State *L) {
216+ b_uint r = andaux(L);
217+ lua_pushunsigned(L, r);
218+ return 1;
219+}
220+
221+
222+static int b_test (lua_State *L) {
223+ b_uint r = andaux(L);
224+ lua_pushboolean(L, r != 0);
225+ return 1;
226+}
227+
228+
229+static int b_or (lua_State *L) {
230+ int i, n = lua_gettop(L);
231+ b_uint r = 0;
232+ for (i = 1; i <= n; i++)
233+ r |= luaL_checkunsigned(L, i);
234+ lua_pushunsigned(L, trim(r));
235+ return 1;
236+}
237+
238+
239+static int b_xor (lua_State *L) {
240+ int i, n = lua_gettop(L);
241+ b_uint r = 0;
242+ for (i = 1; i <= n; i++)
243+ r ^= luaL_checkunsigned(L, i);
244+ lua_pushunsigned(L, trim(r));
245+ return 1;
246+}
247+
248+
249+static int b_not (lua_State *L) {
250+ b_uint r = ~luaL_checkunsigned(L, 1);
251+ lua_pushunsigned(L, trim(r));
252+ return 1;
253+}
254+
255+
256+static int b_shift (lua_State *L, b_uint r, int i) {
257+ if (i < 0) { /* shift right? */
258+ i = -i;
259+ r = trim(r);
260+ if (i >= LUA_NBITS) r = 0;
261+ else r >>= i;
262+ }
263+ else { /* shift left */
264+ if (i >= LUA_NBITS) r = 0;
265+ else r <<= i;
266+ r = trim(r);
267+ }
268+ lua_pushunsigned(L, r);
269+ return 1;
270+}
271+
272+
273+static int b_lshift (lua_State *L) {
274+ return b_shift(L, luaL_checkunsigned(L, 1), luaL_checkint(L, 2));
275+}
276+
277+
278+static int b_rshift (lua_State *L) {
279+ return b_shift(L, luaL_checkunsigned(L, 1), -luaL_checkint(L, 2));
280+}
281+
282+
283+static int b_arshift (lua_State *L) {
284+ b_uint r = luaL_checkunsigned(L, 1);
285+ int i = luaL_checkint(L, 2);
286+ if (i < 0 || !(r & ((b_uint)1 << (LUA_NBITS - 1))))
287+ return b_shift(L, r, -i);
288+ else { /* arithmetic shift for 'negative' number */
289+ if (i >= LUA_NBITS) r = ALLONES;
290+ else
291+ r = trim((r >> i) | ~(~(b_uint)0 >> i)); /* add signal bit */
292+ lua_pushunsigned(L, r);
293+ return 1;
294+ }
295+}
296+
297+
298+static int b_rot (lua_State *L, int i) {
299+ b_uint r = luaL_checkunsigned(L, 1);
300+ i &= (LUA_NBITS - 1); /* i = i % NBITS */
301+ r = trim(r);
302+ r = (r << i) | (r >> (LUA_NBITS - i));
303+ lua_pushunsigned(L, trim(r));
304+ return 1;
305+}
306+
307+
308+static int b_lrot (lua_State *L) {
309+ return b_rot(L, luaL_checkint(L, 2));
310+}
311+
312+
313+static int b_rrot (lua_State *L) {
314+ return b_rot(L, -luaL_checkint(L, 2));
315+}
316+
317+
318+/*
319+** get field and width arguments for field-manipulation functions,
320+** checking whether they are valid
321+*/
322+static int fieldargs (lua_State *L, int farg, int *width) {
323+ int f = luaL_checkint(L, farg);
324+ int w = luaL_optint(L, farg + 1, 1);
325+ luaL_argcheck(L, 0 <= f, farg, "field cannot be negative");
326+ luaL_argcheck(L, 0 < w, farg + 1, "width must be positive");
327+ if (f + w > LUA_NBITS)
328+ luaL_error(L, "trying to access non-existent bits");
329+ *width = w;
330+ return f;
331+}
332+
333+
334+static int b_extract (lua_State *L) {
335+ int w;
336+ b_uint r = luaL_checkunsigned(L, 1);
337+ int f = fieldargs(L, 2, &w);
338+ r = (r >> f) & mask(w);
339+ lua_pushunsigned(L, r);
340+ return 1;
341+}
342+
343+
344+static int b_replace (lua_State *L) {
345+ int w;
346+ b_uint r = luaL_checkunsigned(L, 1);
347+ b_uint v = luaL_checkunsigned(L, 2);
348+ int f = fieldargs(L, 3, &w);
349+ int m = mask(w);
350+ v &= m; /* erase bits outside given width */
351+ r = (r & ~(m << f)) | (v << f);
352+ lua_pushunsigned(L, r);
353+ return 1;
354+}
355+
356+
357+static const luaL_Reg bitlib[] = {
358+ {"arshift", b_arshift},
359+ {"band", b_and},
360+ {"bnot", b_not},
361+ {"bor", b_or},
362+ {"bxor", b_xor},
363+ {"btest", b_test},
364+ {"extract", b_extract},
365+ {"lrotate", b_lrot},
366+ {"lshift", b_lshift},
367+ {"replace", b_replace},
368+ {"rrotate", b_rrot},
369+ {"rshift", b_rshift},
370+ {NULL, NULL}
371+};
372+
373+int luaopen_bit32 (lua_State *L) {
374+ luaL_newlib(L, bitlib);
375+ return 1;
376+}
377+
378+#undef trim
379+
A RD_Client/src/framework/luaengine/lbitlib.h +30-0 View file
@@ -0,0 +1,30 @@
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 LBITLIB_520_BACKPORT4_H
24+#define LBITLIB_520_BACKPORT4_H
25+
26+struct lua_State;
27+
28+int luaopen_bit32 (lua_State *L);
29+
30+#endif
A RD_Client/src/framework/luaengine/luabinder.h +216-0 View file
@@ -0,0 +1,216 @@
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 LUABINDER_H
24+#define LUABINDER_H
25+
26+// this file is and must be included only from luainterface.h
27+#include "luainterface.h"
28+#include "luaexception.h"
29+
30+#include <framework/stdext/traits.h>
31+#include <tuple>
32+
33+/// This namespace contains some dirty metaprogamming that uses a lot of C++0x features
34+/// The purpose here is to create templates that can bind any function from C++
35+/// and expose in lua environment. This is done combining variadic templates,
36+/// lambdas, tuples and some type traits features from the new C++0x standard to create
37+/// templates that can detect functions's arguments and then generate lambdas. These lambdas
38+/// pops arguments from lua stack, call the bound C++ function and then
39+/// pushes the result to lua.
40+namespace luabinder
41+{
42+ /// Pack arguments from lua stack into a tuple recursively
43+ template<int N>
44+ struct pack_values_into_tuple {
45+ template<typename Tuple>
46+ static void call(Tuple& tuple, LuaInterface* lua) {
47+ typedef typename std::tuple_element<N-1, Tuple>::type ValueType;
48+ std::get<N-1>(tuple) = lua->polymorphicPop<ValueType>();
49+ pack_values_into_tuple<N-1>::call(tuple, lua);
50+ }
51+ };
52+ template<>
53+ struct pack_values_into_tuple<0> {
54+ template<typename Tuple>
55+ static void call(Tuple& tuple, LuaInterface* lua) { }
56+ };
57+
58+ /// C++ function caller that can push results to lua
59+ template<typename Ret, typename F, typename... Args>
60+ typename std::enable_if<!std::is_void<Ret>::value, int>::type
61+ call_fun_and_push_result(const F& f, LuaInterface* lua, const Args&... args) {
62+ Ret ret = f(args...);
63+ int numRets = lua->polymorphicPush(ret);
64+ return numRets;
65+ }
66+
67+ /// C++ void function caller
68+ template<typename Ret, typename F, typename... Args>
69+ typename std::enable_if<std::is_void<Ret>::value, int>::type
70+ call_fun_and_push_result(const F& f, LuaInterface* lua, const Args&... args) {
71+ f(args...);
72+ return 0;
73+ }
74+
75+ /// Expand arguments from tuple for later calling the C++ function
76+ template<int N, typename Ret>
77+ struct expand_fun_arguments {
78+ template<typename Tuple, typename F, typename... Args>
79+ static int call(const Tuple& tuple, const F& f, LuaInterface* lua, const Args&... args) {
80+ return expand_fun_arguments<N-1,Ret>::call(tuple, f, lua, std::get<N-1>(tuple), args...);
81+ }
82+ };
83+ template<typename Ret>
84+ struct expand_fun_arguments<0,Ret> {
85+ template<typename Tuple, typename F, typename... Args>
86+ static int call(const Tuple& tuple, const F& f, LuaInterface* lua, const Args&... args) {
87+ return call_fun_and_push_result<Ret>(f, lua, args...);
88+ }
89+ };
90+
91+ /// Bind different types of functions generating a lambda
92+ template<typename Ret, typename F, typename Tuple>
93+ LuaCppFunction bind_fun_specializer(const F& f) {
94+ enum { N = std::tuple_size<Tuple>::value };
95+ return [=](LuaInterface* lua) -> int {
96+ while(lua->stackSize() != N) {
97+ if(lua->stackSize() < N)
98+ g_lua.pushNil();
99+ else
100+ g_lua.pop();
101+ }
102+ Tuple tuple;
103+ pack_values_into_tuple<N>::call(tuple, lua);
104+ return expand_fun_arguments<N,Ret>::call(tuple, f, lua);
105+ };
106+ }
107+
108+ /// Bind a customized function
109+ inline
110+ LuaCppFunction bind_fun(const std::function<int(LuaInterface*)>& f) {
111+ return f;
112+ }
113+
114+ /// Bind a std::function
115+ template<typename Ret, typename... Args>
116+ LuaCppFunction bind_fun(const std::function<Ret(Args...)>& f) {
117+ typedef typename std::tuple<typename stdext::remove_const_ref<Args>::type...> Tuple;
118+ return bind_fun_specializer<typename stdext::remove_const_ref<Ret>::type,
119+ decltype(f),
120+ Tuple>(f);
121+ }
122+
123+ /// Specialization for lambdas
124+ template<typename F>
125+ struct bind_lambda_fun;
126+
127+ template<typename Lambda, typename Ret, typename... Args>
128+ struct bind_lambda_fun<Ret(Lambda::*)(Args...) const> {
129+ static LuaCppFunction call(const Lambda& f) {
130+ typedef typename std::tuple<typename stdext::remove_const_ref<Args>::type...> Tuple;
131+ return bind_fun_specializer<typename stdext::remove_const_ref<Ret>::type,
132+ decltype(f),
133+ Tuple>(f);
134+
135+ }
136+ };
137+
138+ template<typename Lambda>
139+ typename std::enable_if<std::is_constructible<decltype(&Lambda::operator())>::value, LuaCppFunction>::type bind_fun(const Lambda& f) {
140+ typedef decltype(&Lambda::operator()) F;
141+ return bind_lambda_fun<F>::call(f);
142+ }
143+
144+ /// Convert to C++ functions pointers to std::function then bind
145+ template<typename Ret, typename... Args>
146+ LuaCppFunction bind_fun(Ret (*f)(Args...)) {
147+ return bind_fun(std::function<Ret(Args...)>(f));
148+ }
149+
150+ /// Create member function lambdas
151+ template<typename Ret, typename C, typename... Args>
152+ std::function<Ret(const stdext::shared_object_ptr<C>&, const Args&...)> make_mem_func(Ret (C::* f)(Args...)) {
153+ auto mf = std::mem_fn(f);
154+ return [=](const stdext::shared_object_ptr<C>& obj, const Args&... args) mutable -> Ret {
155+ if(!obj)
156+ throw LuaException("failed to call a member function because the passed object is nil");
157+ return mf(obj.get(), args...);
158+ };
159+ }
160+ template<typename C, typename... Args>
161+ std::function<void(const stdext::shared_object_ptr<C>&, const Args&...)> make_mem_func(void (C::* f)(Args...)) {
162+ auto mf = std::mem_fn(f);
163+ return [=](const stdext::shared_object_ptr<C>& obj, const Args&... args) mutable -> void {
164+ if(!obj)
165+ throw LuaException("failed to call a member function because the passed object is nil");
166+ mf(obj.get(), args...);
167+ };
168+ }
169+
170+ /// Create member function lambdas for singleton classes
171+ template<typename Ret, typename C, typename... Args>
172+ std::function<Ret(const Args&...)> make_mem_func_singleton(Ret (C::* f)(Args...), C* instance) {
173+ auto mf = std::mem_fn(f);
174+ return [=](Args... args) mutable -> Ret { return mf(instance, args...); };
175+ }
176+ template<typename C, typename... Args>
177+ std::function<void(const Args&...)> make_mem_func_singleton(void (C::* f)(Args...), C* instance) {
178+ auto mf = std::mem_fn(f);
179+ return [=](Args... args) mutable -> void { mf(instance, args...); };
180+ }
181+
182+
183+ /// Bind member functions
184+ template<typename C, typename Ret, class FC, typename... Args>
185+ LuaCppFunction bind_mem_fun(Ret (FC::* f)(Args...)) {
186+ typedef typename std::tuple<stdext::shared_object_ptr<FC>, typename stdext::remove_const_ref<Args>::type...> Tuple;
187+ auto lambda = make_mem_func<Ret,FC>(f);
188+ return bind_fun_specializer<typename stdext::remove_const_ref<Ret>::type,
189+ decltype(lambda),
190+ Tuple>(lambda);
191+ }
192+
193+ /// Bind singleton member functions
194+ template<typename C, typename Ret, class FC, typename... Args>
195+ LuaCppFunction bind_singleton_mem_fun(Ret (FC::*f)(Args...), C *instance) {
196+ typedef typename std::tuple<typename stdext::remove_const_ref<Args>::type...> Tuple;
197+ VALIDATE(instance);
198+ auto lambda = make_mem_func_singleton<Ret,FC>(f, static_cast<FC*>(instance));
199+ return bind_fun_specializer<typename stdext::remove_const_ref<Ret>::type,
200+ decltype(lambda),
201+ Tuple>(lambda);
202+ }
203+
204+ /// Bind customized member functions
205+ template<typename C>
206+ LuaCppFunction bind_mem_fun(int (C::*f)(LuaInterface*)) {
207+ auto mf = std::mem_fn(f);
208+ return [=](LuaInterface* lua) mutable -> int {
209+ auto obj = lua->castValue<stdext::shared_object_ptr<C>>(1);
210+ lua->remove(1);
211+ return mf(obj, lua);
212+ };
213+ }
214+}
215+
216+#endif
A RD_Client/src/framework/luaengine/luaexception.cpp +53-0 View file
@@ -0,0 +1,53 @@
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 "luaexception.h"
24+#include "luainterface.h"
25+
26+LuaException::LuaException(const std::string& error, int traceLevel)
27+{
28+ //g_lua.clearStack(); // on every exception, clear lua stack
29+ generateLuaErrorMessage(error, traceLevel);
30+}
31+
32+void LuaException::generateLuaErrorMessage(const std::string& error, int traceLevel)
33+{
34+ // append trace level to error message
35+ if(traceLevel >= 0)
36+ m_what = stdext::format("LUA ERROR: %s", g_lua.traceback(error, traceLevel));
37+ else
38+ m_what = stdext::format("LUA ERROR:\n%s", error);
39+}
40+
41+LuaBadNumberOfArgumentsException::LuaBadNumberOfArgumentsException(int expected, int got)
42+{
43+ std::string error = "attempt to call a function with wrong number of arguments";
44+ if(expected >= 0 && got >= 0)
45+ error = stdext::format("%s (expected %d, but got %d)", error, expected, got);
46+ generateLuaErrorMessage(error, 1);
47+}
48+
49+LuaBadValueCastException::LuaBadValueCastException(const std::string& luaTypeName, const std::string& cppTypeName)
50+{
51+ std::string error = stdext::format("attempt to cast a '%s' lua value to '%s'", luaTypeName, cppTypeName);
52+ generateLuaErrorMessage(error, 0);
53+}
A RD_Client/src/framework/luaengine/luaexception.h +56-0 View file
@@ -0,0 +1,56 @@
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 LUAEXCEPTION_H
24+#define LUAEXCEPTION_H
25+
26+#include "declarations.h"
27+
28+class LuaException : public stdext::exception
29+{
30+public:
31+ LuaException(const std::string& error, int traceLevel = -1);
32+ virtual ~LuaException() throw() { }
33+
34+ void generateLuaErrorMessage(const std::string& error, int traceLevel);
35+
36+ virtual const char* what() const throw() { return m_what.c_str(); }
37+
38+protected:
39+ LuaException() { }
40+
41+ std::string m_what;
42+};
43+
44+class LuaBadNumberOfArgumentsException : public LuaException
45+{
46+public:
47+ LuaBadNumberOfArgumentsException(int expected = -1, int got = -1);
48+};
49+
50+class LuaBadValueCastException : public LuaException
51+{
52+public:
53+ LuaBadValueCastException(const std::string& luaTypeName, const std::string& cppTypeName);
54+};
55+
56+#endif
Top