diff --git a/.gitignore b/.gitignore index b9aaf3e7..61a66690 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,7 @@ build/visualstudio/ipch build/visualstudio/*.log build/visualstudio/*.XGD # Emscripten cruft +a.out* doc/samples/*.html doc/samples/*.wasm doc/samples/*.wasm.* diff --git a/doc/tutorial/17_net.cpp b/doc/tutorial/17_net.cpp new file mode 100644 index 00000000..8785d314 --- /dev/null +++ b/doc/tutorial/17_net.cpp @@ -0,0 +1,55 @@ +// +// Lol Engine — HTTP client sample +// +// Copyright © 2016—2020 Sam Hocevar +// +// Lol Engine is free software. It comes without any warranty, to +// the extent permitted by applicable law. You can redistribute it +// and/or modify it under the terms of the Do What the Fuck You Want +// to Public License, Version 2, as published by the WTFPL Task Force. +// See http://www.wtfpl.net/ for more details. +// + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +class demo : public lol::entity +{ +public: + virtual bool init_game() override + { + // Choose URL that works with CORS (cross-origin resource sharing) + client.get("https://api.github.com/"); + return true; + } + + virtual void tick_game(float seconds) override + { + entity::tick_game(seconds); + + if (client.get_status() == lol::net::http::status::success) + { + lol::msg::info("Downloaded %d bytes: %s\n", + (int)client.get_result().size(), + client.get_result().c_str()); + client.reset(); + } + } + + lol::net::http::client client; +}; + +int main(int argc, char **argv) +{ + lol::sys::init(argc, argv); + + lol::Application app("Tutorial 17: HTTP", lol::ivec2(800, 600), 60.0f); + auto p = new demo(); + app.Run(); + + return 0; +} + diff --git a/doc/tutorial/17_net.vcxproj b/doc/tutorial/17_net.vcxproj new file mode 100644 index 00000000..63eb4c70 --- /dev/null +++ b/doc/tutorial/17_net.vcxproj @@ -0,0 +1,66 @@ + + + + $(SolutionDir)\lol + $(SolutionDir) + + + + Debug + NX64 + + + Debug + ORBIS + + + Debug + Win32 + + + Debug + x64 + + + Release + NX64 + + + Release + ORBIS + + + Release + Win32 + + + Release + x64 + + + + + + + + + {46910277-18E3-4151-9AD9-DC5B551772A9} + Application + Win32Proj + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/doc/tutorial/Makefile.am b/doc/tutorial/Makefile.am index 5635d563..e4b23e6a 100644 --- a/doc/tutorial/Makefile.am +++ b/doc/tutorial/Makefile.am @@ -11,7 +11,7 @@ tutorials += 01_triangle 02_cube 03_noise 04_texture 05_easymesh \ 06_sprite 07_input 08_fbo 09_sound 11_fractal \ 12_voronoi 13_shader_builder 14_lua 15_gui endif -tutorials += 16_movie +tutorials += 16_movie 17_net 01_triangle_SOURCES = 01_triangle.cpp 01_triangle.lolfx 01_triangle_CPPFLAGS = $(AM_CPPFLAGS) @@ -81,3 +81,7 @@ endif 16_movie_CPPFLAGS = $(AM_CPPFLAGS) 16_movie_DEPENDENCIES = @LOL_DEPS@ +17_net_SOURCES = 17_net.cpp +17_net_CPPFLAGS = $(AM_CPPFLAGS) +17_net_DEPENDENCIES = @LOL_DEPS@ + diff --git a/lol.sln b/lol.sln index efcaf377..b0c67eaa 100644 --- a/lol.sln +++ b/lol.sln @@ -62,6 +62,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "14_lua", "doc\tutorial\14_l EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "16_movie", "doc\tutorial\16_movie.vcxproj", "{5F5714D0-1C3D-4522-A409-214C5A2951AA}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "17_net", "doc\tutorial\17_net.vcxproj", "{46910277-18E3-4151-9AD9-DC5B551772A9}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "09_sound", "doc\tutorial\09_sound.vcxproj", "{51626A22-DD68-4450-9696-13B03BF7A2C5}" EndProject Global @@ -418,6 +420,22 @@ Global {5F5714D0-1C3D-4522-A409-214C5A2951AA}.Release|Win32.Build.0 = Release|Win32 {5F5714D0-1C3D-4522-A409-214C5A2951AA}.Release|Win64.ActiveCfg = Release|x64 {5F5714D0-1C3D-4522-A409-214C5A2951AA}.Release|Win64.Build.0 = Release|x64 + {46910277-18E3-4151-9AD9-DC5B551772A9}.Debug|NX64.ActiveCfg = Debug|NX64 + {46910277-18E3-4151-9AD9-DC5B551772A9}.Debug|NX64.Build.0 = Debug|NX64 + {46910277-18E3-4151-9AD9-DC5B551772A9}.Debug|ORBIS.ActiveCfg = Debug|ORBIS + {46910277-18E3-4151-9AD9-DC5B551772A9}.Debug|ORBIS.Build.0 = Debug|ORBIS + {46910277-18E3-4151-9AD9-DC5B551772A9}.Debug|Win32.ActiveCfg = Debug|Win32 + {46910277-18E3-4151-9AD9-DC5B551772A9}.Debug|Win32.Build.0 = Debug|Win32 + {46910277-18E3-4151-9AD9-DC5B551772A9}.Debug|Win64.ActiveCfg = Debug|x64 + {46910277-18E3-4151-9AD9-DC5B551772A9}.Debug|Win64.Build.0 = Debug|x64 + {46910277-18E3-4151-9AD9-DC5B551772A9}.Release|NX64.ActiveCfg = Release|NX64 + {46910277-18E3-4151-9AD9-DC5B551772A9}.Release|NX64.Build.0 = Release|NX64 + {46910277-18E3-4151-9AD9-DC5B551772A9}.Release|ORBIS.ActiveCfg = Release|ORBIS + {46910277-18E3-4151-9AD9-DC5B551772A9}.Release|ORBIS.Build.0 = Release|ORBIS + {46910277-18E3-4151-9AD9-DC5B551772A9}.Release|Win32.ActiveCfg = Release|Win32 + {46910277-18E3-4151-9AD9-DC5B551772A9}.Release|Win32.Build.0 = Release|Win32 + {46910277-18E3-4151-9AD9-DC5B551772A9}.Release|Win64.ActiveCfg = Release|x64 + {46910277-18E3-4151-9AD9-DC5B551772A9}.Release|Win64.Build.0 = Release|x64 {51626A22-DD68-4450-9696-13B03BF7A2C5}.Debug|NX64.ActiveCfg = Debug|NX64 {51626A22-DD68-4450-9696-13B03BF7A2C5}.Debug|NX64.Build.0 = Debug|NX64 {51626A22-DD68-4450-9696-13B03BF7A2C5}.Debug|ORBIS.ActiveCfg = Debug|ORBIS @@ -463,6 +481,7 @@ Global {81C83B42-D00A-4FA3-9A3D-80F9D46524BF} = {E74CF679-CA2A-47E9-B1F4-3779D6AC6B04} {31B96262-1C41-43B9-BA38-27AA385B05DB} = {E74CF679-CA2A-47E9-B1F4-3779D6AC6B04} {5F5714D0-1C3D-4522-A409-214C5A2951AA} = {E74CF679-CA2A-47E9-B1F4-3779D6AC6B04} + {46910277-18E3-4151-9AD9-DC5B551772A9} = {E74CF679-CA2A-47E9-B1F4-3779D6AC6B04} {51626A22-DD68-4450-9696-13B03BF7A2C5} = {E74CF679-CA2A-47E9-B1F4-3779D6AC6B04} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/src/lol/net/http.h b/src/lol/net/http.h index 3a6e1dc8..42bf18f0 100644 --- a/src/lol/net/http.h +++ b/src/lol/net/http.h @@ -25,15 +25,29 @@ namespace http class client_impl; +enum class status : uint8_t +{ + ready = 0, + pending = 1, + success = 2, + error = 3, +}; + class client { public: client(); ~client(); + // Enqueue a query void get(std::string const &url); - bool is_ready() const; - std::tuple result(); + + // Reset state + void reset(); + + // Get status (may be pending) and result + status get_status() const; + std::string const & get_result() const; private: std::unique_ptr impl; diff --git a/src/net/http.cpp b/src/net/http.cpp index 189ee1f3..67be802a 100644 --- a/src/net/http.cpp +++ b/src/net/http.cpp @@ -10,10 +10,14 @@ // See http://www.wtfpl.net/ for more details. // -#pragma once - #include +#if __EMSCRIPTEN__ +# include +#else +# include +#endif + namespace lol { @@ -23,22 +27,41 @@ namespace net namespace http { -#if __EMSCRIPTEN__ -class client_impl -{ - // FIXME -}; -#else class client_impl { public: +#if __EMSCRIPTEN__ + static void on_success(emscripten_fetch_t *fetch) + { + auto *that = (client_impl *)fetch->userData; + msg::info("finished downloading %llu bytes from URL %s.\n", + fetch->numBytes, fetch->url); + that->m_result.assign(fetch->data, fetch->numBytes); + that->m_status = status::success; + } + + static void on_failure(emscripten_fetch_t *fetch) + { + auto *that = (client_impl *)fetch->userData; + msg::error("downloading %s failed, HTTP failure status code: %d.\n", + fetch->url, fetch->status); + that->m_status = status::error; + } + + emscripten_fetch_t *m_fetch = nullptr; +#else void get(std::string const& url) { } -}; + + //httplib::Client &client; #endif + status m_status; + std::string m_result; +}; + client::client() : impl(std::make_unique()) { @@ -46,20 +69,41 @@ client::client() client::~client() { +#if __EMSCRIPTEN__ + emscripten_fetch_close(impl->m_fetch); +#endif } void client::get(std::string const &url) { + impl->m_status = status::pending; +#if __EMSCRIPTEN__ + emscripten_fetch_attr_t attr; + emscripten_fetch_attr_init(&attr); + strcpy(attr.requestMethod, "GET"); + attr.userData = impl.get(); + attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; + attr.onsuccess = client_impl::on_success; + attr.onerror = client_impl::on_failure; + impl->m_fetch = emscripten_fetch(&attr, url.c_str()); +#else +#endif +} + +void client::reset() +{ + impl->m_status = status::ready; + impl->m_result.assign(""); } -bool client::is_ready() const +status client::get_status() const { - return false; + return impl->m_status; } -std::tuple client::result() +std::string const & client::get_result() const { - return std::make_tuple(404, std::string()); + return impl->m_result; } } // namespace http