-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplay.py
316 lines (257 loc) · 12 KB
/
play.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
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
#!./python3.7/python
import Client , front
from wx import App
from multiprocessing import Process
import logging
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.common.exceptions import TimeoutException, StaleElementReferenceException, NoAlertPresentException
from selenium.webdriver.firefox.firefox_binary import FirefoxBinary
from selenium import webdriver
from re import finditer, sub
import time
# This function gets executed when you run
# python play.py and it serves as a way to run
# the Beer_programming client and parse the user's
# arguments
def main():
logging.basicConfig(format="%(asctime)s %(levelname)s | %(message)s", level=logging.DEBUG)
from sys import argv
Beer_programming(**arg_parse(argv)).play(False)
# arg_parse does take a list of arguments and returns
# one dict with the parameters and values (str) determined
# by the keys and values of arg_dict
# if one key is found, the following argument is
# chosen as a value
def arg_parse(args:list, arg_dict:dict=
{"--ip":"ip","--port":"port",
"-i":"ip","-p":"port"}) -> dict:
final = {}
before = False
for arg in args[1:]:
if(before):
logging.debug(f"Found arg ({before}) : {arg}")
final[before] = arg
before = False
continue
value = arg_dict.get(arg, None)
if(not value is None):
before = value
return final
"""
The class Beer_programming is the main class used to
play the game. It will create a Client, who will then
connect to the server, which will rule the game.
Between its features there is the possibility to
run and compile the chosen online compiler, and to
chat (unused)
"""
class Beer_programming():
def __init__(self, ip:str=None, port:int=12412):
logging.debug(f"BeerProgramming.__init__(self,{ip},{port})")
self.client = Client.Client()
self.listener = None
self.order_dict = {"--compile":self.compile,
"--drink":self.drink,
"--add_player":self.add_player,
"--add_chat":self.add_chat,
"--_print":self.__print_gui,
"--_start":self.start_compiler}
self.players = {}
self.chat = []
self.drinks = 0
self.conn_step = [";;"]
self.conn_symbols = {"Serv_to_Client":";;", "Client_to_Serv":"::",
"Serv_listen":"->", "Serv_send":"<_",
"Client_listen":"<-", "Client_send":"<_",
"Urgency":"!!", "Evaluate":"#"}
if(not ip is None): self.connect(ip,port)
else: self.connect("127.0.0.1", port)
# The play function should be the first function to be
# executed when the server starts to listen to the socket.
# When executed, play will send the server the order to
# add itself as a named user, will ask the server
# what are the names of the other players and then it
# will give the server the control.
# play can open a gui(unused) and will then run
# _play_process
def play(self, gui:bool=True) -> None:
logging.debug(f"BeerProgramming.play(self, {gui})")
name = False
while(not name):
name = input("In-game alias: ")
name = name if input("Confirm?[Y/n] ").lower() in ["y","ye","yes","s","si"] else False
self.name = name
self.client.send_to_server(f"--add_player;{name}{self.conn_symbols['Client_to_Serv']}")
logging.debug("Sent alias to server")
self.client.send_to_server(f"--play{self.conn_symbols['Serv_to_Client']}")
logging.debug("Sent play petition to server")
#self.start_compiler()
if(gui):
play_pro = Process(target=self._play_process)
play_pro.start()
self.gui()
play_pro.join()
else: self._play_process()
# _play_process is the main function to interactuate
# with the server. Based on the actual state of the
# Beer_programming.conn_steps queue it will either
# listen or ask what to send to the server.
# It's the server's job to determine if it should or
# should not need the user's input
def _play_process(self) -> None:
while(len(self.conn_step)):
step = self.conn_step[0]
self.conn_step.pop(0)
if(step == self.conn_symbols["Client_to_Serv"] or step == self.conn_symbols["Client_send"]):
decoded_data = input("> ")
if(step == self.conn_symbols["Evaluate"]): decoded_data = eval(decoded_data)
self.symbol_parse(decoded_data)
self.client.send_to_server(decoded_data)
elif(step == self.conn_symbols["Serv_to_Client"] or step == self.conn_symbols["Client_listen"]):
decoded_data = self.listen()
logging.info(f"Recived data '{decoded_data}' from server")
self.symbol_parse(decoded_data)
decoded_data = sub(f'|'.join(self.conn_symbols.values()),'',decoded_data)
self.data_parse(decoded_data)
logging.debug(f"Conn_steps: {self.conn_step}")
# connect (kind of) overrides Client.connect. Even if
# unnecessary, I think this function makes the code
# cleaner
def connect(self, ip:str, port:int=12412) -> None:
logging.debug(f"BeerProgramming.connect(self, {ip}, {port})")
self.listener = self.client.connect(ip,port)
# listen does make use of the Client's generator to
# listen to the server and return a string
def listen(self, listener:"generator"=None) -> str:
#logging.debug(f"BeerProgramming.listen(self, {listener})")
if(listener is None):
if(self.listener is None): return
listener = self.listener
return(next(listener))
# (unused) gui does open a gui for the user to see
# all clients connected and chat with them
def gui(self) -> None:
logging.debug("BeerProgramming.gui(self)")
app = App()
frame = front.Frame(name="Beer Programming")
panel = frame.new_panel(bgcolor=(50,50,50))
player_panel = panel.new_panel("(%0.49,%1)")
chat_panel = panel.new_panel("(%0.49,%1)","(%0.51,0)")
self.text_list = {self.name:panel.add_text((0,0),"(%1,%0.2)", self.name)}
app.MainLoop()
# start_compiler does start a new selenium instance (gecko)
# controlled by the game to make sure nobody can cheat
# with (at least) one saved file
def start_compiler(self) -> None:
logging.info("Configuration complete. Trying to run the drivers. This could take some time...")
self.driver = webdriver.Firefox(executable_path=(
__file__).replace("play.py", "geckodriver"))
#options=options, firefox_profile=profile,# capabilities=firefox_capabilities,
#firefox_binary=FirefoxBinary((__file__).replace("play.py", "geckodriver")))
logging.info("Drivers ran succesfully!")
self.driver.get("https://www.onlinegdb.com/online_java_compiler")
self.tab = self.driver.current_window_handle
self.driver.find_element_by_xpath("//*[@class='glyphicon glyphicon-menu-left']").click()
self.client.send_to_server("Done")
# data_parse takes any message sent by the server
# and it executes the function assigned as key
# in the self.order_dict dictionary
def data_parse(self, data:str) -> None:
#print(f"data_parse {data}")
order = None
args = ()
for arg in data.split(';'):
new_ord = self.order_dict.get(arg.strip(), None)
print(f"arg:{arg}, new_ord:{new_ord}")
if(not new_ord is None):
if(not order is None):
print(f"{order}{args}")
try:
order(*args)
except Exception as err: print(f"ERROR: {err}")
order = new_ord
args = ()
elif(arg.strip() != ''): args+=(arg.strip(),)
if(not order is None):
print(f"{order}{args}.")
try:
order(*args)
except Exception as err:
print(order)
print(args)
raise err
print(f"ERROR: {err}.")
# symbol_parse is used by the user and the server
# to tell (into a queue) what the Client should do
# next (listen/send something)
def symbol_parse(self, command:str):
urgent = False
num = 0
for symbol in finditer('|'.join(self.conn_symbols.values()), command):
if(symbol.group(0) == self.conn_symbols["Urgency"]):
urgent = True
else:
if(urgent):
self.conn_step.insert(0, symbol.group(0))
urgent = False
num += 1
else:
self.conn_step.append(symbol.group(0))
#
# Game related functions
#
# compile will make use of the selenium instance to
# try to compile the code. Any error in the code will
# be sent to the server, who will answer how
# many times will have the user to drink
def compile(self) -> int:
self.driver.switch_to.window(self.tab)
try: self.driver.switch_to.alert.dismiss()
except NoAlertPresentException: pass
while(True):
try:
self.driver.switch_to.window(self.tab)
self.driver.find_elements_by_xpath('''//*[@class='glyphicon glyphicon-stop']''').click()
except: pass
try:
self.driver.find_element_by_xpath("//*[@class='glyphicon glyphicon-play']").click()
break
except: pass
time.sleep(3)
self.driver.switch_to.window(self.tab)
try:
self.client.send_to_server(f"--drink;{len(self.driver.find_elements_by_xpath('''//*[@class='error_line']'''))}"
f"{self.conn_symbols['Urgency']}{self.conn_symbols['Client_listen']}")
except:
self.client.send_to_server(f"--drink;0{self.conn_symbols['Urgency']}{self.conn_symbols['Client_listen']}")
self.driver.switch_to.window(self.tab)
# drink will be executed by the server when the code is
# compiled. It will then tell the user how many times
# it should drink
def drink(self, drinks:str='0'):
logging.info(f"Recieved order to drink {drinks} times")
self.driver.execute_script(f"alert('Drink {drinks} times');", [])
# add_player will record a new player, when given by the
# server. It is meant to have the utility of listing
# all users avaliable to chat with
def add_player(self, player:"str(addr, name)"):
addr, name = eval(player)
logging.info(f"Added new player {name} ({addr})")
self.players[addr] = name
# add_chat adds a new message to the chat list
def add_chat(self, text:str):
self.chat.append(eval(text))
# __print_gui is a debugging function that prints all
# server-recieved variables
def __print_gui(self):
print()
print(f"Chat: {self.chat}")
print()
print(f"Players: {self.players}")
print()
print(f"Drinks:{self.drinks}")
print()
if __name__ == "__main__":
main()