| @@ -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 | + | } |
| @@ -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 |
| @@ -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 | + | } |
| @@ -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 |
| @@ -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 |
| @@ -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 | + |
| @@ -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 |
| @@ -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)>; |
| @@ -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 |
| @@ -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 | + | }; |
| @@ -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 |
| @@ -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 | + | }; |
| @@ -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 | + | } |
| @@ -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; |
| @@ -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 |
| @@ -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 | + |
| @@ -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 |
| @@ -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 |
| @@ -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 | + | } |
| @@ -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 |