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 "application.h"
24#include <csignal>
25#include <framework/core/clock.h>
26#include <framework/core/resourcemanager.h>
27#include <framework/core/modulemanager.h>
28#include <framework/core/eventdispatcher.h>
29#include <framework/core/configmanager.h>
30#include "asyncdispatcher.h"
31#include <framework/luaengine/luainterface.h>
32#include <framework/platform/crashhandler.h>
33#include <framework/platform/platform.h>
34#include <framework/http/http.h>
35
36#if not(defined(ANDROID) || defined(FREE_VERSION))
37#include <boost/process.hpp>
38#endif
39
40#include <locale>
41
42#include <framework/net/connection.h>
43#include <framework/proxy/proxy.h>
44
45void exitSignalHandler(int sig)
46{
47 static bool signaled = false;
48 switch(sig) {
49 case SIGTERM:
50 case SIGINT:
51 if(!signaled && !g_app.isStopping() && !g_app.isTerminated()) {
52 signaled = true;
53 g_dispatcher.addEvent(std::bind(&Application::close, &g_app));
54 }
55 break;
56 }
57}
58
59Application::Application()
60{
61 m_appName = "application";
62 m_appCompactName = "app";
63 m_appVersion = "none";
64 m_charset = "cp1252";
65 m_stopping = false;
66#ifdef ANDROID
67 m_mobile = true;
68#endif
69}
70
71void Application::init(std::vector<std::string>& args)
72{
73 // capture exit signals
74 signal(SIGTERM, exitSignalHandler);
75 signal(SIGINT, exitSignalHandler);
76
77 // setup locale
78 std::locale::global(std::locale());
79
80 // process args encoding
81 g_platform.processArgs(args);
82
83 g_asyncDispatcher.init();
84
85 std::string startupOptions;
86 for(uint i=1;i<args.size();++i) {
87 const std::string& arg = args[i];
88 startupOptions += " ";
89 startupOptions += arg;
90 }
91 if(startupOptions.length() > 0)
92 g_logger.info(stdext::format("Startup options: %s", startupOptions));
93
94 m_startupOptions = startupOptions;
95
96 // mobile testing
97 if (startupOptions.find("-mobile") != std::string::npos)
98 m_mobile = true;
99
100 // initialize configs
101 g_configs.init();
102
103 // initialize lua
104 g_lua.init();
105 registerLuaFunctions();
106
107 // initalize proxy
108 g_proxy.init();
109}
110
111void Application::deinit()
112{
113 g_lua.callGlobalField("g_app", "onTerminate");
114
115 // run modules unload events
116 g_modules.unloadModules();
117 g_modules.clear();
118
119 // release remaining lua object references
120 g_lua.collectGarbage();
121
122 // poll remaining events
123 poll();
124
125 // disable dispatcher events
126 g_dispatcher.shutdown();
127}
128
129void Application::terminate()
130{
131 // terminate network
132 Connection::terminate();
133
134 // release configs
135 g_configs.terminate();
136
137 // release resources
138 g_resources.terminate();
139
140 // terminate script environment
141 g_lua.terminate();
142
143 // terminate proxy
144 g_proxy.terminate();
145
146 m_terminated = true;
147
148 signal(SIGTERM, SIG_DFL);
149 signal(SIGINT, SIG_DFL);
150}
151
152void Application::poll()
153{
154 Connection::poll();
155
156 g_dispatcher.poll();
157
158 // poll connection again to flush pending write
159 Connection::poll();
160}
161
162void Application::exit()
163{
164 g_lua.callGlobalField<bool>("g_app", "onExit");
165 m_stopping = true;
166}
167
168void Application::quick_exit()
169{
170#ifdef _MSC_VER
171 ::quick_exit(0);
172#else
173 ::exit(0);
174#endif
175}
176
177void Application::close()
178{
179 if(!g_lua.callGlobalField<bool>("g_app", "onClose"))
180 exit();
181}
182
183void Application::restart()
184{
185#if not(defined(ANDROID) || defined(FREE_VERSION))
186 boost::process::child c(g_resources.getBinaryName());
187 std::error_code ec2;
188 if (c.wait_for(std::chrono::seconds(1), ec2)) {
189 g_logger.fatal("Updater restart error. Please restart application");
190 }
191 c.detach();
192 quick_exit();
193#else
194 exit();
195#endif
196}
197
198void Application::restartArgs(const std::vector<std::string>& args)
199{
200#if not(defined(ANDROID) || defined(FREE_VERSION))
201 boost::process::child c(g_resources.getBinaryName(), boost::process::args(args));
202 std::error_code ec2;
203 if (c.wait_for(std::chrono::seconds(1), ec2)) {
204 g_logger.fatal("Updater restart error. Please restart application");
205 }
206 c.detach();
207 quick_exit();
208#else
209 exit();
210#endif
211}
212
213std::string Application::getOs()
214{
215#if defined(ANDROID)
216 return "android";
217#elif defined(WIN32)
218 return "windows";
219#elif defined(__APPLE__)
220 return "mac";
221#elif __linux
222 return "linux";
223#else
224 return "unknown";
225#endif
226}
227
228