File: ServerFramework.cpp

package info (click to toggle)
freeorion 0.5.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 194,940 kB
  • sloc: cpp: 186,508; python: 40,969; ansic: 1,164; xml: 719; makefile: 32; sh: 7
file content (357 lines) | stat: -rw-r--r-- 12,978 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#include "ServerFramework.h"

#include "ServerWrapper.h"
#include "../python/SetWrapper.h"
#include "../python/CommonWrappers.h"

#include "../util/Logger.h"
#include "../util/Directories.h"
#include "../universe/Condition.h"
#include "../universe/Universe.h"

#include <boost/filesystem.hpp>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include <boost/python/suite/indexing/map_indexing_suite.hpp>
#include <boost/python/docstring_options.hpp>
#include <boost/python/stl_iterator.hpp>

#include <stdexcept>

namespace fs = boost::filesystem;
namespace py = boost::python;


BOOST_PYTHON_MODULE(freeorion) {
    py::docstring_options doc_options(true, true, false);

    FreeOrionPython::WrapGameStateEnums();
    FreeOrionPython::WrapGalaxySetupData();
    FreeOrionPython::WrapEmpire();
    FreeOrionPython::WrapUniverseClasses();
    FreeOrionPython::WrapServer();
    FreeOrionPython::WrapConfig();

    // STL Containers
    py::class_<std::vector<int>>("IntVec")
        .def(py::vector_indexing_suite<std::vector<int>>())
    ;
    py::class_<std::vector<std::string>>("StringVec")
        .def(py::vector_indexing_suite<std::vector<std::string>>())
    ;

    py::class_<std::map<int, bool>>("IntBoolMap")
        .def(py::map_indexing_suite<std::map<int, bool>>())
    ;

    FreeOrionPython::SetWrapper<std::set<int>>::Wrap("IntSet");
    FreeOrionPython::SetWrapper<std::set<std::string>>::Wrap("StringSet");
}


auto PythonServer::InitImports() -> bool
{
    DebugLogger() << "Initializing server Python imports";
    // Allow the "freeorion" C++ module to be imported within Python code
    return PyImport_AppendInittab("freeorion", &PyInit_freeorion) != -1;
}

auto PythonServer::InitModules() -> bool
{
    DebugLogger() << "Initializing server Python modules";

    // Confirm existence of the directory containing the universe generation
    // Python scripts and add it to Pythons sys.path to make sure Python will
    // find our scripts
    auto python_universe_generator_dir = GetPythonUniverseGeneratorDir();
    if (!fs::exists(python_universe_generator_dir)) {
        ErrorLogger() << "Can't find folder containing universe generation scripts: " << PathToString(python_universe_generator_dir);
        return false;
    }
    AddToSysPath(python_universe_generator_dir);

    // Confirm existence of the directory containing the turn event Python
    // scripts and add it to Pythons sys.path to make sure Python will find
    // our scripts
    auto python_turn_events_dir = GetPythonTurnEventsDir();
    if (!fs::exists(python_turn_events_dir)) {
        ErrorLogger() << "Can't find folder containing turn events scripts:" << PathToString(python_turn_events_dir);
        return false;
    }
    AddToSysPath(python_turn_events_dir);

    // import universe generator script file
    m_python_module_turn_events = py::import("turn_events");

    // Confirm existence of the directory containing the auth Python scripts
    // and add it to Pythons sys.path to make sure Python will find our scripts
    auto python_auth_dir = GetPythonAuthDir();
    if (!fs::exists(python_auth_dir)) {
        ErrorLogger() << "Can't find folder containing auth scripts:" << PathToString(python_auth_dir);
        return false;
    }
    AddToSysPath(python_auth_dir);

    // import auth script file
    m_python_module_auth = py::import("auth");

    // Save AuthProvider instance in auth module's namespace
    m_python_module_auth.attr("__dict__")["auth_provider"] = m_python_module_auth.attr("AuthProvider")();

    AddToSysPath(GetPythonChatDir());

    // import chat script file
    m_python_module_chat = py::import("chat");

    // Save ChatProvider instance in chat module's namespace
    m_python_module_chat.attr("__dict__")["chat_history_provider"] = m_python_module_chat.attr("ChatHistoryProvider")();

    // Save asyncio event loop
    if (GetOptionsDB().Get<int>("network.server.python.asyncio-interval") > 0) {
        py::object asyncio = py::import("asyncio");

        m_asyncio_event_loop = asyncio.attr("new_event_loop")();
	asyncio.attr("set_event_loop")(m_asyncio_event_loop);
    }

    DebugLogger() << "Server Python modules successfully initialized!";
    return true;
}

auto PythonServer::IsRequireAuthOrReturnRoles(const std::string& player_name, const std::string& ip_address,
                                              bool &result, Networking::AuthRoles& roles) const -> bool
{
    py::object auth_provider = m_python_module_auth.attr("__dict__")["auth_provider"];
    if (!auth_provider) {
        ErrorLogger() << "Unable to get Python object auth_provider";
        return false;
    }
    py::object f = auth_provider.attr("is_require_auth_or_return_roles");
    if (!f) {
        ErrorLogger() << "Unable to call Python method is_require_auth";
        return false;
    }
    roles.Clear();
    py::object r = f(player_name, ip_address);
    py::extract<py::list> py_roles(r);
    if (py_roles.check()) {
        result = false;
        py::stl_input_iterator<Networking::RoleType> role_begin(py_roles), role_end;
        for (auto& it = role_begin; it != role_end; ++it)
            roles.SetRole(*it, true);
    } else {
        result = py::extract<bool>(r)();
    }
    return true;
}

auto PythonServer::IsSuccessAuthAndReturnRoles(const std::string& player_name, const std::string& auth,
                                               bool &result, Networking::AuthRoles& roles) const -> bool
{
    py::object auth_provider = m_python_module_auth.attr("__dict__")["auth_provider"];
    if (!auth_provider) {
        ErrorLogger() << "Unable to get Python object auth_provider";
        return false;
    }
    py::object f = auth_provider.attr("is_success_auth_and_return_roles");
    if (!f) {
        ErrorLogger() << "Unable to call Python method is_success_auth";
        return false;
    }
    py::object r = f(player_name, auth);
    py::extract<py::list> py_roles(r);
    if (py_roles.check()) {
        result = true;

        py::stl_input_iterator<Networking::RoleType> role_begin(py_roles), role_end;
        for (auto& it = role_begin; it != role_end; ++it)
             roles.SetRole(*it, true);
    } else {
        result = false;
        DebugLogger() << "Wrong auth data for \"" << player_name << "\": check returns " << py::extract<std::string>(py::str(r))();
    }
    return true;
}

auto PythonServer::FillListPlayers(std::vector<PlayerSetupData>& players) const -> bool
{
    const py::object auth_provider = m_python_module_auth.attr("__dict__")["auth_provider"];
    if (!auth_provider) {
        ErrorLogger() << "Unable to get Python object auth_provider";
        return false;
    }
    const py::object f = auth_provider.attr("list_players");
    if (!f) {
        ErrorLogger() << "Unable to call Python method list_players";
        return false;
    }
    const py::object r = f();
    const py::extract<py::list> py_players(r);
    if (py_players.check()) {
        py::stl_input_iterator<PlayerSetupData> players_begin(py_players), players_end;
        players.reserve(py::len(py_players));
        players.insert(players.end(), players_begin, players_end);
    } else {
        DebugLogger() << "Wrong players list data: check returns "
                      << py::extract<std::string>(py::str(r))();
        return false;
    }
    return true;
}

auto PythonServer::GetPlayerDelegation(const std::string& player_name,
                                       std::vector<std::string>& result) const -> bool
{
    py::object auth_provider = m_python_module_auth.attr("__dict__")["auth_provider"];
    if (!auth_provider) {
        ErrorLogger() << "Unable to get Python object auth_provider";
        return false;
    }
    py::object f = auth_provider.attr("get_player_delegation");
    if (!f) {
        ErrorLogger() << "Unable to call Python method get_player_delegation";
        return false;
    }
    py::object r = f(player_name);
    py::extract<py::list> py_players(r);
    if (py_players.check()) {
        py::stl_input_iterator<std::string> players_begin(py_players), players_end;
        result.insert(result.end(), players_begin, players_end);
    } else {
        DebugLogger() << "Wrong delegated players list data: check returns "
                      << py::extract<std::string>(py::str(r))();
        return false;
    }

    return true;
}

auto PythonServer::LoadChatHistory(boost::circular_buffer<ChatHistoryEntity>& chat_history) -> bool
{
    py::object chat_provider = m_python_module_chat.attr("__dict__")["chat_history_provider"];
    if (!chat_provider) {
        ErrorLogger() << "Unable to get Python object chat_history_provider";
        return false;
    }
    py::object f = chat_provider.attr("load_history");
    if (!f) {
        ErrorLogger() << "Unable to call Python method load_history";
        return false;
    }
    py::object r = f();
    py::extract<py::list> py_history(r);
    if (py_history.check()) {
        py::stl_input_iterator<py::tuple> entity_begin(py_history), entity_end;
        for (auto& it = entity_begin; it != entity_end; ++it) {
            ChatHistoryEntity e;
            e.timestamp = boost::posix_time::from_time_t(py::extract<time_t>((*it)[0]));;
            e.player_name = py::extract<std::string>((*it)[1]);
            e.text = py::extract<std::string>((*it)[2]);
            py::tuple color = py::extract<py::tuple>((*it)[3]);
            e.text_color = std::array<uint8_t, 4>{{
                py::extract<uint8_t>(color[0]),
                py::extract<uint8_t>(color[1]),
                py::extract<uint8_t>(color[2]),
                py::extract<uint8_t>(color[3])
            }};
            chat_history.push_back(e);
        }
    }

    return true;
}

auto PythonServer::PutChatHistoryEntity(const ChatHistoryEntity& chat_history_entity) -> bool
{
    py::object chat_provider = m_python_module_chat.attr("__dict__")["chat_history_provider"];
    if (!chat_provider) {
        ErrorLogger() << "Unable to get Python object chat_history_provider";
        return false;
    }
    py::object f = chat_provider.attr("put_history_entity");
    if (!f) {
        ErrorLogger() << "Unable to call Python method put_history_entity";
        return false;
    }

    auto color = chat_history_entity.text_color;

    return f(py::long_(to_time_t(chat_history_entity.timestamp)),
             chat_history_entity.player_name,
             chat_history_entity.text,
             py::make_tuple(std::get<0>(color), std::get<1>(color), std::get<2>(color), std::get<3>(color)));
}

auto PythonServer::CreateUniverse(std::map<int, PlayerSetupData>& player_setup_data) -> bool
{
    // Confirm existence of the directory containing the universe generation
    // Python scripts and add it to Pythons sys.path to make sure Python will
    // find our scripts
    auto python_universe_generator_dir = GetPythonUniverseGeneratorDir();
    if (!fs::exists(python_universe_generator_dir)) {
        ErrorLogger() << "Can't find folder containing universe generation scripts: " << PathToString(python_universe_generator_dir);
        return false;
    }
    AddToSysPath(python_universe_generator_dir);

    // import universe generator script file
    m_python_module_universe_generator = py::import("universe_generator");

    py::dict py_player_setup_data;

    // the universe generator module should contain an "error_report" function,
    // so set the ErrorReport member function to use it
    SetErrorModule(m_python_module_universe_generator);

    for (auto& psd : player_setup_data) {
        py_player_setup_data[psd.first] = py::object(psd.second);
    }

    py::object f = m_python_module_universe_generator.attr("create_universe");
    if (!f) {
        ErrorLogger() << "Unable to call Python function create_universe ";
        return false;
    }

    return f(py_player_setup_data);
}

auto PythonServer::ExecuteTurnEvents() -> bool
{
    py::object f = m_python_module_turn_events.attr("execute_turn_events");
    if (!f) {
        ErrorLogger() << "Unable to call Python function execute_turn_events ";
        return false;
    }
    return f();
}

auto PythonServer::AsyncIOTick() -> bool
{
    py::object stop = m_asyncio_event_loop.attr("stop");
    if (!stop) {
        ErrorLogger() << "Unable to call Python function stop";
        return false;
    }
    stop();
    py::object run_forever = m_asyncio_event_loop.attr("run_forever");
    if (!run_forever) {
        ErrorLogger() << "Unable to call Python function run_forever";
        return false;
    }
    run_forever();

    return true;
}

auto GetPythonUniverseGeneratorDir() -> fs::path
{ return GetPythonDir() / "universe_generation"; }

auto GetPythonTurnEventsDir() -> fs::path
{ return GetPythonDir() / "turn_events"; }

auto GetPythonAuthDir() -> fs::path
{ return GetPythonDir() / "auth"; }

auto GetPythonChatDir() -> fs::path
{ return GetPythonDir() / "chat"; }