1from io import StringIO
2
3import freeOrionAIInterface as fo
4from freeorion_tools import chat_human
5from code import InteractiveInterpreter
6import logging
7import sys
8from logging import error
9
10interpreter = InteractiveInterpreter({'fo': fo})
11debug_mode = False
12
13
14RED = '<rgba 255 0 0 255>%s</rgba>'
15WHITE = '<rgba 255 255 255 255>%s</rgba>'
16ENTERING_DEBUG_MESSAGE = 'Entering debug mode'
17
18
19def handle_debug_chat(sender, message):
20    global debug_mode
21    human_id = [x for x in fo.allPlayerIDs() if fo.playerIsHost(x)][0]
22    ais = [x for x in fo.allPlayerIDs() if not fo.playerIsHost(x)]
23    is_debug_chat = False
24    if message == ENTERING_DEBUG_MESSAGE:
25        is_debug_chat = True
26    if sender != human_id:
27        return is_debug_chat  # don't chat with bots
28    elif message == 'stop':
29        is_debug_chat = True
30        if debug_mode:
31            chat_human("exiting debug mode")
32        debug_mode = False
33    elif debug_mode:
34        print('>', message, end='')
35        is_debug_chat = True
36        out, err = [x.strip('\n') for x in shell(message)]
37        if out:
38            chat_human(WHITE % out)
39        if err:
40            chat_human(RED % err)
41    elif message.startswith('start'):
42        is_debug_chat = True
43        try:
44            player_id = int(message[5:].strip())
45        except ValueError as e:
46            error(e)
47            chat_human(str(e))
48            return True
49        if player_id == fo.playerID():
50            debug_mode = True
51
52            initial_code = [
53                'from aistate_interface import get_aistate',
54            ]
55
56            # add some variables to scope: (name, help text, value)
57            scopes_variable = (
58                ('ai', 'aistate', 'get_aistate()'),
59                ('u', 'universe', 'fo.getUniverse()'),
60                ('e', 'empire', 'fo.getEmpire()'),
61            )
62            for var, _, code in scopes_variable:
63                initial_code.append('%s = %s' % (var, code))
64
65            shell(';'.join(initial_code))
66
67            variable_template = '<u><rgba 255 255 0 255>%s</rgba></u>%s %s'
68            variables = (variable_template % (var, ' ' * (3 - len(var)), name) for var, name, _ in scopes_variable)
69            chat_human(WHITE % "%s\n"
70                               "Print <rgba 255 255 0 255>'stop'</rgba> to exit.\n"
71                               "Local variables:\n"
72                               "  %s" % (ENTERING_DEBUG_MESSAGE, '\n  '.join(variables)))
73
74    elif message == 'help':
75        is_debug_chat = True
76        if ais[0] == fo.playerID():
77            chat_human(WHITE % "Chat commands:")
78            chat_human(WHITE % "  <u><rgba 0 255 255 255>start id</rgba></u>: start debug for selected empire")
79            chat_human(WHITE % "  <u><rgba 0 255 255 255>stop</rgba></u>: stop debug")
80            chat_human(WHITE % "Empire ids:")
81            for player in fo.allPlayerIDs():
82                if not fo.playerIsHost(player):
83                    chat_human('  <rgba {0.colour.r} {0.colour.g} {0.colour.b} {0.colour.a}>id={0.empireID} empire_name={0.name}</rgba> player_name={1}'.format(fo.getEmpire(fo.playerEmpireID(player)), fo.playerName(player)))
84    return is_debug_chat
85
86
87def shell(msg):
88    old_stdout = sys.stdout
89    old_stderr = sys.stderr
90
91    sys.stdout = StringIO()
92    sys.stderr = StringIO()
93    handler = logging.StreamHandler(sys.stdout)
94    logging.getLogger().addHandler(handler)
95
96    interpreter.runsource(msg)
97
98    logging.getLogger().removeHandler(handler)
99
100    sys.stdout.seek(0)
101    out = sys.stdout.read()
102    sys.stderr.seek(0)
103    err = sys.stderr.read()
104    sys.stdout = old_stdout
105    sys.stderr = old_stderr
106    return out, err
107