forked from Lexxie9952/fcw.org-server
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcivcom.py
executable file
·197 lines (169 loc) · 7.09 KB
/
civcom.py
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
# -*- coding: utf-8 -*-
'''
Freeciv - Copyright (C) 2009-2014 - Andreas Røsdal [email protected]
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
'''
import socket
import select
from struct import *
from threading import Thread
import logging
import time
HOST = '127.0.0.1'
logger = logging.getLogger("freeciv-proxy")
# The CivCom handles communication between freeciv-proxy and the Freeciv C
# server.
class CivCom(Thread):
def __init__(self, username, civserverport, key, civwebserver):
Thread.__init__(self)
self.socket = None
self.username = username
self.civserverport = civserverport
self.key = key
self.send_buffer = []
self.connect_time = time.time()
self.civserver_messages = []
self.stopped = False
self.packet_size = -1
self.net_buf = bytearray(0)
self.header_buf = bytearray(0)
self.daemon = True
self.civwebserver = civwebserver
def run(self):
# setup connection to civserver
if (logger.isEnabledFor(logging.INFO)):
logger.info("Start connection to civserver for " + self.username)
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setblocking(True)
self.socket.settimeout(2)
try:
self.socket.connect((HOST, self.civserverport))
self.socket.settimeout(0.01)
except socket.error as reason:
self.send_error_to_client(
"Proxy unable to connect to civserver. Error: %s" %
(reason))
return
# send initial login packet to civserver
self.civserver_messages = [self.civwebserver.loginpacket]
self.send_packets_to_civserver()
# receive packets from server
while True:
packet = self.read_from_connection()
if (self.stopped):
return
if (packet is not None):
self.net_buf += packet
if (len(self.net_buf) == self.packet_size and self.net_buf[-1] == 0):
# valid packet received from freeciv server, send it to
# client.
self.send_buffer_append(self.net_buf[:-1])
self.packet_size = -1
self.net_buf = bytearray(0)
continue
time.sleep(0.01)
# prevent max CPU usage in case of error
def read_from_connection(self):
try:
if (self.socket is not None and not self.stopped):
if (self.packet_size == -1):
self.header_buf += self.socket.recv(2 -
len(self.header_buf))
if (len(self.header_buf) == 0):
self.close_connection()
return None
if (len(self.header_buf) == 2):
header_pck = unpack('>H', self.header_buf)
self.header_buf = bytearray(0)
self.packet_size = header_pck[0] - 2
if (self.packet_size <= 0 or self.packet_size > 32767):
logger.error("Invalid packet size " + str(self.packet_size))
else:
# complete header not read yet. return now, and read
# the rest next time.
return None
if (self.socket is not None and self.net_buf is not None and self.packet_size > 0):
data = self.socket.recv(self.packet_size - len(self.net_buf))
if (len(data) == 0):
self.close_connection()
return None
return data
except socket.timeout:
self.send_packets_to_client()
self.send_packets_to_civserver()
return None
except OSError:
return None
def close_connection(self):
if (logger.isEnabledFor(logging.INFO)):
logger.info(
"Server connection closed. Removing civcom thread for " +
self.username)
if (hasattr(self.civwebserver, "civcoms") and self.key in list(self.civwebserver.civcoms.keys())):
del self.civwebserver.civcoms[self.key]
if (self.socket is not None):
self.socket.close()
self.socket = None
self.civwebserver = None
self.stopped = True
# queue messages to be sent to client.
def send_buffer_append(self, data):
try:
self.send_buffer.append(
data.decode(
encoding="utf-8",
errors="ignore"))
except UnicodeDecodeError:
if (logger.isEnabledFor(logging.ERROR)):
logger.error(
"Unable to decode string from civcom socket, for user: " +
self.username)
return
# sends packets to client (WebSockets client / browser)
def send_packets_to_client(self):
packet = self.get_client_result_string()
if (packet is not None and self.civwebserver is not None):
# Calls the write_message callback on the next Tornado I/O loop iteration (thread safely).
self.civwebserver.io_loop.add_callback(lambda: self.civwebserver.write_message(packet))
def get_client_result_string(self):
result = ""
try:
if len(self.send_buffer) > 0:
result = "[" + ",".join(self.send_buffer) + "]"
else:
result = None
finally:
del self.send_buffer[:]
return result
def send_error_to_client(self, message):
if (logger.isEnabledFor(logging.ERROR)):
logger.error(message)
self.send_buffer_append(
("{\"pid\":25,\"event\":100,\"message\":\"" + message + "\"}").encode("utf-8"))
# Send packets from freeciv-proxy to civserver
def send_packets_to_civserver(self):
if (self.civserver_messages is None or self.socket is None):
return
try:
for net_message in self.civserver_messages:
utf8_encoded = net_message.encode('utf-8')
header = pack('>H', len(utf8_encoded) + 3)
self.socket.sendall(
header +
utf8_encoded +
b'\0')
except:
self.send_error_to_client(
"Proxy unable to communicate with civserver on port " + str(self.civserverport))
finally:
self.civserver_messages = []
# queue message for the civserver
def queue_to_civserver(self, message):
self.civserver_messages.append(message)