Issues (229)

client/main_client.py (3 issues)

1
#
2
#  Copyright 2001 - 2016 Ludek Smid [http://www.ospace.net/]
3
#
4
#  This file is part of Outer Space.
5
#
6
#  Outer Space is free software; you can redistribute it and/or modify
7
#  it under the terms of the GNU General Public License as published by
8
#  the Free Software Foundation; either version 2 of the License, or
9
#  (at your option) any later version.
10
#
11
#  Outer Space is distributed in the hope that it will be useful,
12
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
#  GNU General Public License for more details.
15
#
16
#  You should have received a copy of the GNU General Public License
17
#  along with Outer Space; if not, write to the Free Software
18
#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
#
20
21
# some default values as a workaround [midpoint]
22
import binascii
23
import os, os.path
24
import random
25
import re
26
import time
27
import sys
28
29
"""
30
To save people trouble with installing PyGame, which might be a bit of
31
a challenge for non-technical users, we will install it ourselves.
32
Even though it is mentioned in the Installation guide, we want to make
33
sure user agrees with the installation.
34
"""
35
try:
36
    import pygame
37
except ImportError:
38
    import pip
39
    print("Outer Space client requires PyGame to work properly.")
40
    print("PyGame will be installed in 10 seconds")
41
    print("You can cancel the installation by pressing CTRL+C or by closing this window")
42
    for i in xrange(10,0,-1):
43
        print i
44
        time.sleep(1)
45
    pip.main(['install','pygame','--user'])
46
    # reload needs to happen, so we can import right away
47
    import site
48
    reload(site)
49
    import pygame
50
51
import pygame.image, pygame.ftfont, pygame.time, pygame.version
52
import pygame.transform
53
54
from osci.config import Config
55
import osci.gdata as gdata
56
import ige.version
57
from ige import log
58
import osci
59
import resources
60
import osci.res
61
62
63
64
def defineBackground():
65
    surface = pygame.Surface.copy(gdata.screen)
66
    image = random.choice([
67
        resources.get('bck1_1024x768.jpg'),
68
        resources.get('bck2_1024x768.jpg'),
69
        resources.get('bck3_1024x768.jpg'),
70
        resources.get('bck4_1024x768.jpg'),
71
        resources.get('bck5_1024x768.jpg'),
72
    ])
73
    background = pygame.image.load(image).convert_alpha()
74
    backgroundOffset = (
75
        (surface.get_width() - background.get_width()) / 2,
76
        (surface.get_height() - background.get_height()) / 2,
77
    )
78
    try:
79
        font = pygame.ftfont.Font(resources.get('fonts/DejaVuLGCSans.ttf'), 12)
80
    except IOError:
81
        # this can happen on windows during update process, when directory
82
        # is moved already
83
        # TODO: proper fix is to use pygameui and caching
84
        font = pygame.ftfont.Font(None, 12)
85
    font.set_bold(1)
86
    color = 0x40, 0x70, 0x40
87
    #
88
    surface.blit(background, backgroundOffset)
89
    # screen.fill((0x00, 0x00, 0x00))
90
    # OSCI version
91
    img = font.render(_('Outer Space %s') % ige.version.versionString, 1, color)
92
    surface.blit(img, (5, surface.get_height() - 4 * img.get_height() - 5))
93
    # Pygame version
94
    img = font.render(_('Pygame %s') % pygame.version.ver, 1, color)
95
    surface.blit(img, (5, surface.get_height() - 3 * img.get_height() - 5))
96
    # Python version
97
    img = font.render(_('Python %s') % sys.version, 1, color)
98
    surface.blit(img, (5, surface.get_height() - 2 * img.get_height() - 5))
99
    # Video driver
100
    w, h = pygame.display.get_surface().get_size()
101
    d = pygame.display.get_surface().get_bitsize()
102
    img = font.render(_('Video Driver: %s [%dx%dx%d]') % (pygame.display.get_driver(), w, h, d), 1, color)
103
    surface.blit(img, (5, surface.get_height() - 1 * img.get_height() - 5))
104
    return surface
105
106
# update function
107
def update():
108
    rects = gdata.app.draw(gdata.screen)
109
    if gdata.cmdInProgress:
110
        img = osci.res.cmdInProgressImg
111
        wx, wy = gdata.screen.get_size()
112
        x, y = img.get_size()
113
        gdata.screen.blit(img, (wx - x, 0))
114
        rects.append(pygame.Rect(wx - x, 0, img.get_width(), img.get_height()))
115
    pygame.display.update(rects)
116
    pygame.event.pump()
117
118
def setDefaults(gdata, options):
119
    if gdata.config.client.language == None:
120
        gdata.config.client.language = 'en'
121
    if gdata.config.defaults.minfleetsymbolsize == None:
122
        gdata.config.defaults.minfleetsymbolsize = 4
123
    if gdata.config.defaults.minplanetsymbolsize == None:
124
        gdata.config.defaults.minplanetsymbolsize = 5
125
    if gdata.config.defaults.maxfleetsymbolsize == None:
126
        gdata.config.defaults.maxfleetsymbolsize = 0
127
    if gdata.config.defaults.maxplanetsymbolsize == None:
128
        gdata.config.defaults.maxplanetsymbolsize = 0
129
    if gdata.config.game.screenshot_dir is None:
130
        gdata.config.game.screenshot_dir = os.path.join(options.configDir, 'screenshots')
131
        try:
132
            os.makedirs(gdata.config.game.screenshot_dir)
133
        except OSError:
134
            pass
135
    # read Highlights
136
    if gdata.config.defaults.colors != None:
137
            for coldef in gdata.config.defaults.colors.split(' '):
138
                m = re.match('(\d+):(0[xX].*?),(0[xX].*?),(0[xX].*)',coldef)
139
                if m != None :
140
                    id = int(m.group(1))
141
                    red = min(int(m.group(2),16),255)
142
                    green = min(int(m.group(3),16),255)
143
                    blue = min(int(m.group(4),16),255)
144
                    gdata.playersHighlightColors[id] = (red,green,blue)
145
                else:
146
                    log.warning('OSCI','Unrecognized highlight definition :',coldef)
147
    # read Object Keys
148
    if gdata.config.defaults.objectkeys != None:
149
            for objectkey in gdata.config.defaults.objectkeys.split(' '):
150
                m = re.match('(\d+):(\d+)',objectkey)
151
                if m != None :
152
                    key = int(m.group(1))
153
                    objid = int(m.group(2))
154
                    gdata.objectFocus[key] = objid
155
                else:
156
                    log.warning('OSCI','Unrecognized object key definition :',objectkey)
157
158
def setSkinTheme(gdata, ui):
159
    theme = "green"
160
    if gdata.config.client.theme != None:
161
            theme = gdata.config.client.theme
162
    ui.SkinableTheme.enableMusic(gdata.config.defaults.music == "yes")
163
    ui.SkinableTheme.enableSound(gdata.config.defaults.sound == "yes")
164
    ui.SkinableTheme.setSkin(os.path.join(resources.get("themes"), theme))
165
    ui.SkinableTheme.loadMusic(gdata.config.defaults.mymusic)
166
    if gdata.config.defaults.musicvolume:
167
            ui.SkinableTheme.setMusicVolume(float(gdata.config.defaults.musicvolume)/ 100.0)
168
    if gdata.config.defaults.soundvolume:
169
            ui.SkinableTheme.setVolume(float(gdata.config.defaults.soundvolume) / 100.0)
170
171
    gdata.sevColors[gdata.CRI] = (ui.SkinableTheme.themeCritical)
172
    gdata.sevColors[gdata.MAJ] = (ui.SkinableTheme.themeMajor)
173
    gdata.sevColors[gdata.MIN] = (ui.SkinableTheme.themeMinor)
174
    gdata.sevColors[gdata.NONE] = (ui.SkinableTheme.themeNone)
175
    gdata.sevColors[gdata.DISABLED] = (ui.SkinableTheme.themeDisabled)
176
177
def runClient(options):
178
179
    # log initialization
180
    log.message("Starting Outer Space Client", ige.version.versionString)
181
    log.debug("sys.path =", sys.path)
182
    log.debug("os.name =", os.name)
183
    log.debug("sys.platform =", sys.platform)
184
    log.debug("os.getcwd() =", os.getcwd())
185
    log.debug("sys.frozen =", getattr(sys, "frozen", None))
186
187
    # create required directories
188
    if not os.path.exists(options.configDir):
189
        os.makedirs(options.configDir)
190
    log.debug("options.configDir =", options.configDir)
191
192
    running = 1
193
    first = True
194
    #while running:
195
    if not first:
196
        reload(osci)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable reload does not seem to be defined.
Loading history...
197
    # parse configuration
198
    if first:
199
        import osci.gdata as gdata
200
    else:
201
        reload(gdata)
202
203
    gdata.config = Config(os.path.join(options.configDir, options.configFilename))
204
    gdata.config.game.server = options.server
205
206
    setDefaults(gdata, options)
207
208
    language = gdata.config.client.language
209
    import gettext
210
    log.debug('OSCI', 'Installing translation for:', language)
211
    if language == 'en':
212
        log.debug('OSCI', 'English is native - installing null translations')
213
        tran = gettext.NullTranslations()
214
    else:
215
        try:
216
            tran = gettext.translation('OSPACE', resources.get('translations'), languages = [language])
217
        except IOError:
218
            log.warning('OSCI', 'Cannot find catalog for', language)
219
            log.message('OSCI', 'Installing null translations')
220
            tran = gettext.NullTranslations()
221
222
    tran.install(unicode = 1)
223
224
225
    #initialize pygame and prepare screen
226
    if (gdata.config.defaults.sound == "yes") or (gdata.config.defaults.music == "yes"):
227
            pygame.mixer.pre_init(44100, -16, 2, 4096)
228
229
    os.environ['SDL_VIDEO_ALLOW_SCREENSAVER'] = '1'
230
    os.environ['SDL_DEBUG'] = '1'
231
    pygame.init()
232
233
    flags = pygame.SWSURFACE
234
235
    DEFAULT_SCRN_SIZE = (800, 600)
236
    gdata.scrnSize = DEFAULT_SCRN_SIZE
237
    if gdata.config.display.resolution == "FULLSCREEN":
238
            gdata.scrnSize = (0, 0)
239
            flags |= pygame.FULLSCREEN
240
    elif gdata.config.display.resolution is not None:
241
            width, height = gdata.config.display.resolution.split('x')
242
            gdata.scrnSize = (int(width), int(height))
243
244
    if gdata.config.display.depth == None:
245
            # guess best depth
246
            bestdepth = pygame.display.mode_ok(gdata.scrnSize, flags)
247
    else:
248
            bestdepth = int(gdata.config.display.depth)
249
250
    # initialize screen
251
    try:
252
        screen = pygame.display.set_mode(gdata.scrnSize, flags, bestdepth)
253
        # gdata.scrnSize is used everywhere to setup windows
254
        gdata.scrnSize = screen.get_size()
255
    except pygame.error:
256
        # for example if fullscreen is selected with resolution bigger than display
257
        # TODO: as of now, fullscreen has automatic resolution
258
        gdata.scrnSize = DEFAULT_SCRN_SIZE
259
        screen = pygame.display.set_mode(gdata.scrnSize, flags, bestdepth)
260
    gdata.screen = screen
261
    log.debug('OSCI', 'Driver:', pygame.display.get_driver())
262
    log.debug('OSCI', 'Using depth:', bestdepth)
263
    log.debug('OSCI', 'Display info:', pygame.display.Info())
264
265
    pygame.mouse.set_visible(1)
266
267
    pygame.display.set_caption(_('Outer Space %s') % ige.version.versionString)
268
269
    # set icon
270
    pygame.display.set_icon(pygame.image.load(resources.get('icon48.png')).convert_alpha())
271
272
    # UI stuff
273
    if first:
274
            import pygameui as ui
275
    else:
276
            reload(ui)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable ui does not seem to be defined.
Loading history...
277
278
    setSkinTheme(gdata, ui)
279
280
    app = ui.Application(update, theme = ui.SkinableTheme)
281
    app.background = defineBackground()
282
    app.draw(gdata.screen)
283
    app.windowSurfaceFlags = pygame.SWSURFACE | pygame.SRCALPHA
284
    gdata.app = app
285
286
    pygame.event.clear()
287
288
    # resources
289
    import osci.res
290
291
    osci.res.initialize()
292
293
    # load resources
294
    import osci.dialog
295
    dlg = osci.dialog.ProgressDlg(gdata.app)
296
    osci.res.loadResources(dlg)
297
    dlg.hide()
298
    osci.res.prepareUIIcons(ui.SkinableTheme.themeIcons)
299
300
301
    while running:
302
        if first:
303
            import osci.client, osci.handler
304
            from igeclient.IClient import IClientException
305
        else:
306
            reload(osci.client)
307
            reload(osci.handler)
308
        osci.client.initialize(gdata.config.game.server, osci.handler, options)
309
310
        # create initial dialogs
311
        if first:
312
            import osci.dialog
313
        else:
314
            reload(osci.dialog)
315
        gdata.savePassword = gdata.config.game.lastpasswordcrypted != None
316
317
        if options.login and options.password:
318
            gdata.config.game.lastlogin = options.login
319
            gdata.config.game.lastpassword = options.password
320
            gdata.config.game.lastpasswordcrypted = binascii.b2a_base64(options.password).strip()
321
            gdata.config.game.autologin = 'yes'
322
            gdata.savePassword = 'no'
323
324
        loginDlg = osci.dialog.LoginDlg(gdata.app)
325
        updateDlg = osci.dialog.UpdateDlg(gdata.app)
326
327
        # event loop
328
        update()
329
330
        lastSave = time.clock()
331
        # set counter to -1 to trigger Update dialog (see "if" below)
332
        counter = -1
333
        needsRefresh = False
334
        session = 1
335
        first = False
336
        while running and session:
337
            try:
338
                counter += 1
339
                if counter == 0:
340
                    # display initial dialog in the very first cycle
341
                    updateDlg.display(caller = loginDlg, options = options)
342
                # process as many events as possible before updating
343
                evt = pygame.event.wait()
344
                evts = pygame.event.get()
345
                evts.insert(0, evt)
346
347
                forceKeepAlive = False
348
                saveDB = False
349
350
                for evt in evts:
351
                    if evt.type == pygame.QUIT:
352
                        running = 0
353
                        break
354
                    if evt.type == (ui.USEREVENT) and evt.action == "localExit":
355
                        session = False
356
                        break
357
                    if evt.type == pygame.ACTIVEEVENT:
358
                        if evt.gain == 1 and evt.state == 6:
359
                            # pygame desktop window focus event
360
                            needsRefresh = True
361
                    if evt.type == pygame.KEYUP and evt.key == pygame.K_F12:
362
                        if not pygame.key.get_mods() & pygame.KMOD_CTRL:
363
                            running = 0
364
                            break
365
                    if evt.type == pygame.KEYUP and evt.key == pygame.K_F9:
366
                        forceKeepAlive = True
367
                    evt = gdata.app.processEvent(evt)
368
369
                if gdata.app.needsUpdate() or needsRefresh:
370
                    needsRefresh = False
371
                    update()
372
                # keep alive connection
373
                osci.client.keepAlive(forceKeepAlive)
374
375
                # save DB every 4 hours in case of a computer crash
376
                # using "counter" to limit calls to time.clock() to approximately every 10-15 minutes
377
                if counter > 5000:
378
                    # set this to zero so we don't display Update dialog
379
                    counter = 0
380
                    if time.clock() - lastSave > 14400:
381
                        saveDB = True
382
                if saveDB:
383
                    osci.client.saveDB()
384
                    lastSave = time.clock();
385
386
            except IClientException, e:
0 ignored issues
show
The variable IClientException does not seem to be defined for all execution paths.
Loading history...
387
                osci.client.reinitialize()
388
                gdata.app.setStatus(e.args[0])
389
                loginDlg.display(message = e.args[0])
390
            except Exception, e:
391
                log.warning('OSCI', 'Exception in event loop')
392
                if not isinstance(e, SystemExit) and not isinstance(e, KeyboardInterrupt):
393
                    log.debug("Processing exception")
394
                    # handle exception
395
                    import traceback, StringIO
396
                    fh = StringIO.StringIO()
397
                    exctype, value, tb = sys.exc_info()
398
                    funcs = [entry[2] for entry in traceback.extract_tb(tb)]
399
                    faultID = "%06d-%03d" % (
400
                        hash("/".join(funcs)) % 1000000,
401
                        traceback.extract_tb(tb)[-1][1] % 1000,
402
                    )
403
                    del tb
404
                    # high level info
405
                    print >>fh, "Exception ID:", faultID
406
                    print >>fh
407
                    print >>fh, "%s: %s" % (exctype, value)
408
                    print >>fh
409
                    print >>fh, "--- EXCEPTION DATA ---"
410
                    # dump exception
411
                    traceback.print_exc(file = fh)
412
                    excDlg = osci.dialog.ExceptionDlg(gdata.app)
413
                    excDlg.display(faultID, fh.getvalue())
414
                    del excDlg # reference to the dialog holds app's intance
415
                    fh.close()
416
                    del fh
417
                else:
418
                    break
419
420
        # write configuration
421
        log.debug("Saving configuration.")
422
        # Save highlights
423
        hl = ""
424
        for playerID in gdata.playersHighlightColors.keys():
425
            color = gdata.playersHighlightColors[playerID]
426
            r = hex(color[0])
427
            g = hex(color[1])
428
            b = hex(color[2])
429
            hl = "%s %s:%s,%s,%s" % (hl,playerID,r,g,b)
430
        gdata.config.defaults.colors = hl
431
        # Save objects
432
        of = ""
433
        for keyNum in gdata.objectFocus.keys():
434
            objid = gdata.objectFocus[keyNum]
435
            of = "%s %s:%s" % (of,keyNum,objid)
436
        gdata.config.defaults.objectkeys = of
437
        #
438
        if gdata.savePassword == False:
439
            gdata.config.game.lastpasswordcrypted = None
440
        gdata.config.save()
441
442
        # logout
443
        osci.client.logout()
444
445
    log.debug("Shut down")
446
    return osci.client
447