Issues (229)

client/osci/client.py (6 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
import os.path
22
23
from igeclient import IClient, IClientDB
24
from ige.ospace import Rules
25
import ige.ospace.Const as Const
26
from ige.IDataHolder import IDataHolder
27
import ige, gdata, osci, math, time
28
from ige import log
29
30
# module globals
31
cmdProxy = None
32
db = None
33
callbackObj = None
34
lastUpdate = -1
35
server = None
36
serverVersion = None
37
ignoreMsgs = {}
38
nonexistingObj = {}
39
options = None
40
account = None
41
42
def initialize(aServer, aCallbackObj, anOptions):
43
    global callbackObj, server, options
44
    callbackObj = aCallbackObj
45
    server = aServer
46
47
    options = anOptions
48
    initCmdProxy(options.heartbeat)
49
50
def reinitialize():
51
    global cmdProxy
52
    cmdProxy = None
53
    initCmdProxy(options.heartbeat)
54
55
def initCmdProxy(keepAliveTime):
56
    global cmdProxy, server
57
    if not cmdProxy:
58
        callbackObj.onInitConnection()
59
        proxy = None
60
        if gdata.config.proxy.http != None:
61
            proxy = gdata.config.proxy.http
62
        cmdProxy = IClient.IClient(server, proxy, msgHandler, idleHandler, 'OSClient/%s' % ige.version.versionString, keepAliveTime)
63
        callbackObj.onConnInitialized()
64
        cmdProxy.connect()
65
66
## Authentication
67
68
def login(gameid, login, password):
69
    global account
70
    if gdata.config.client.keepAlive != None:
71
        cmdProxy.keepAliveTime = int(gdata.config.client.keepAlive)
72
    if cmdProxy.login(gameid, login, password):
73
        account = cmdProxy.getAccountData()
74
        return 1
75
    return 0
76
77
def createAccount(login, password, nick, email):
78
    global cmdProxy, server
79
    if not cmdProxy:
80
        callbackObj.onInitConnection()
81
        proxy = None
82
        if gdata.config.proxy.http != None:
83
            proxy = gdata.config.proxy.http
84
        cmdProxy = IClient.IClient(server, proxy, msgHandler, idleHandler, 'OSClient/%d.%d.%d%s' % osci.version)
85
        cmdProxy.connect(login)
86
        if gdata.config.client.keepAlive != None:
87
            cmdProxy.keepAliveTime = int(gdata.config.client.keepAlive)
88
        callbackObj.onConnInitialized()
89
    return cmdProxy.createAccount(login, password, nick, email)
90
91
def logout():
92
    global db, lastUpdate, account
93
    if cmdProxy and cmdProxy.logged:
94
        cmdProxy.logout()
95
    saveDB()
96
    db = None
97
    account = None
98
    lastUpdate = -1
99
100
def saveDB():
101
    if db:
102
        log.message('OSClient', 'Saving database')
103
        db.save()
104
105
    ## Message handler
106
107
def msgHandler(mid, data):
108
    if ignoreMsgs.has_key(mid):
109
        log.debug('OSClient', 'ignoring message', mid, data)
110
        return
111
    if mid == Const.SMESSAGE_NEWTURN:
112
        updateDatabase()
113
    elif mid == Const.SMESSAGE_NEWMESSAGE:
114
        getMessages()
115
    elif mid == IClient.MSG_CMD_BEGIN:
116
        callbackObj.onCmdBegin()
117
    elif mid == IClient.MSG_CMD_END:
118
        callbackObj.onCmdEnd()
119
    else:
120
        log.debug('OSClient', 'unhandled message', mid, data)
121
122
def messageIgnore(mid):
123
    global ignoreMsgs
124
    ignoreMsgs[mid] = None
125
126
def messageEnable(mid):
127
    global ignoreMsgs
128
    if ignoreMsgs.has_key(mid):
129
        del ignoreMsgs[mid]
130
131
## Idle handler
132
def idleHandler():
133
    callbackObj.onWaitingForResponse()
134
135
## Updater
136
137
def updateDatabase(clearDB = 0):
138
    try:
139
        return updateDatabaseUnsafe(clearDB)
140
    except:
141
        log.warning("Cannot update database")
142
    # again with clear
143
    callbackObj.onUpdateFinished()
144
    messageEnable(Const.SMESSAGE_NEWTURN)
145
    messageEnable(Const.SMESSAGE_NEWMESSAGE)
146
    return updateDatabaseUnsafe(clearDB = 1, force = 1)
147
148
def updateDatabaseUnsafe(clearDB = 0, force = 0):
149
    """Update database by fetching data from the server."""
150
    global lastUpdate, nonexistingObj, db
151
    # get real turn
152
    result = cmdProxy.getIntroInfo(Const.OID_UNIVERSE)
153
    if not db:
154
        dbLocation = os.path.join(options.configDir, 'player_data')
155
        db = IClientDB.IClientDB(result.cid, result.turn, dbLocation, cmdProxy.gameID)
156
    if clearDB:
157
        db.clear()
158
    db.turn = result.turn
159
    #
160
    if db.turn <= lastUpdate and not force:
161
        return
162
    log.message('IClient', 'Updating...')
163
    lastUpdate = db.turn
164
    nonexistingObj.clear()
165
    # start updating...
166
    messageIgnore(Const.SMESSAGE_NEWTURN)
167
    messageIgnore(Const.SMESSAGE_NEWMESSAGE)
168
    callbackObj.onUpdateStarting()
169
    current = 0
170
    max = 1
171
    # compute total objects to be fetched
172
    max += 6 # clear map, get messages, ...
173
    current += 1
174
    callbackObj.onUpdateProgress(current, max, _("Deleting obsolete data..."))
175
    # delete selected objects
176
    # reset combatCounters
177
    for objID in db.keys():
178
        obj = db[objID]
179
        if hasattr(obj, "combatCounter"):
180
            obj.combatCounter = 0
181 View Code Duplication
        if not hasattr(obj, 'type'):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
182
            del db[objID]
183
        elif obj.type == Const.T_FLEET:
184
            del db[objID]
185
        elif hasattr(obj, 'owner') and obj.owner == db.playerID \
186
            and objID != db.playerID:
187
            # delete player's objects
188
            del db[objID]
189
        else:
190
            if hasattr(obj, "scanPwr"): obj.scanPwr = 0
191
            if hasattr(obj, "scannerPwr"): obj.scannerPwr = 0
192
    # update player
193
    current += 1
194
    callbackObj.onUpdateProgress(current, max, _("Downloading player data..."))
195
    db[db.playerID] = get(db.playerID)
196
    player = db[db.playerID]
197
    # update from scanner's map
198
    current += 1
199
    callbackObj.onUpdateProgress(current, max, _("Updating scanner..."))
200
    map = cmdProxy.getScannerMap(db.playerID)
201
    for objID in map:
202
        db[objID] = map[objID]
203
    # update player's planets and fleets
204
    current += 1
205
    callbackObj.onUpdateProgress(current, max, _("Downloading planets and fleets data..."))
206
    for obj in cmdProxy.multiGetInfo(1, player.planets[:] + player.fleets[:]):
207
        db[obj.oid] = obj
208
    # TODO: try to load allies's info
209
    # get messages from server
210
    current += 1
211
    callbackObj.onUpdateProgress(current, max, _("Downloading messages..."))
212
    getMessages()
213
    log.message('IClient', 'Update finished.')
214
    callbackObj.onUpdateFinished()
215
    messageEnable(Const.SMESSAGE_NEWTURN)
216
    messageEnable(Const.SMESSAGE_NEWMESSAGE)
217
218
## Basic functions
219
220
def keepAlive(force = False):
221
    if cmdProxy:
222
        try:
223
            if force or cmdProxy.doKeepAlive():
224
                if db:
225
                    # client is logged into the game
226
                    getMessages()
227
                else:
228
                    # client is in player selection / galaxy booking phase
229
                    cmdProxy.keepAlive()
230
        except ige.NoAccountException:
231
            pass
232
233 View Code Duplication
def get(objID, forceUpdate = 0, noUpdate = 0, canBePublic = 1, publicOnly = 0):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
234
    global nonexistingObj
235
    if nonexistingObj.has_key(objID) and not forceUpdate:
236
        return None
237
    if noUpdate:
238
        return db.get(objID, None)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable db does not seem to be defined.
Loading history...
239
    if (db.needsUpdate(objID) or forceUpdate) and not publicOnly:
240
        try:
241
            db[objID] = cmdProxy.getInfo(objID)
242
        except ige.SecurityException:
243
            if canBePublic:
244
                db[objID] = cmdProxy.getPublicInfo(objID)
245
            else:
246
                return db.get(objID, None)
247
        except ige.NoSuchObjectException:
248
            if db.has_key(objID):
249
                del db[objID]
250
            nonexistingObj[objID] = None
251
            return None
252
    if (db.needsUpdate(objID) or forceUpdate) and publicOnly: #for when the data you need is never anything but public
253
        try:
254
            db[objID] = cmdProxy.getPublicInfo(objID)
255
        except ige.NoSuchObjectException:
256
            if db.has_key(objID):
257
                del db[objID]
258
            nonexistingObj[objID] = None
259
            return None
260
    return db[objID]
261
262
def updateIDs(objIDs):
263
    delete = objIDs[:]
264
    for obj in cmdProxy.multiGetInfo(1, objIDs):
265
        db[obj.oid] = obj
266
        delete.remove(obj.oid)
267
    for objID in delete:
268
        if db.has_key(objID):
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable db does not seem to be defined.
Loading history...
269
            del db[objID]
270
271 View Code Duplication
def getRelationTo(objID):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
272
    obj = getPlayer()
273
    if obj.oid == objID:
274
        return Const.REL_UNITY
275
    if objID == Const.OID_NONE:
276
        return Const.REL_UNDEF
277
    dipl = obj.diplomacyRels.get(objID, None)
278
    if dipl:
279
        return dipl.relation
280
    else:
281
        return obj.defaultRelation
282
283
def getTurn():
284
    return db.turn
285
286
def getFullTechInfo(techID):
287
    player = db[db.playerID]
288
    return Rules.techs[techID]
289
290
def getTechInfo(techID):
291
    player = db[db.playerID]
292
    tech = Rules.techs[techID]
293
    # player possess this technology
294
    if techID in player.techs or tech.fullInfo:
295
        return tech
296
297
    # player can research this technology
298
    canResearch = 1
299
    if player.race not in tech.researchRaces:
300
        canResearch = 0
301
    for tmpTechID, improvement in tech.researchRequires:
302
        if tmpTechID not in player.techs or player.techs[tmpTechID] < improvement:
303
            canResearch = 0
304
            break
305
    for stratRes in tech.researchReqSRes:
306
        if player.stratRes.get(stratRes, 0) < 1:
307
            canResearch = 0
308
            break
309
    for tmpTechID in player.techs:
310
        if techID in Rules.techs[tmpTechID].researchDisables:
311
            canResearch = 0
312
            break
313
    if tech.level > player.techLevel:
314
        canResearch = 0
315
316
    if canResearch:
317
        result = IDataHolder()
318
        result.partialData = None
319
        for attr in ["id", 'name', 'isDiscovery', 'isStructure',
320
            'isProject', 'isShipEquip', 'isShipHull', 'researchMod',
321
            'researchTurns', 'textPreRsrch', 'researchRequires', 'subtype',
322
            "researchReqSRes", "researchDisables", "level", "researchRaces"]:
323
            setattr(result, attr, getattr(tech, attr))
324
        return result
325
    # player should know only basic params about tech
326
    result = IDataHolder()
327
    result.partialData = None
328
    for attr in ["id", "name", "researchRequires", "subtype", "level", "researchRaces"]:
329
        setattr(result, attr, getattr(tech, attr))
330
    return result
331
332
def getAllTechIDs():
333
    return Rules.techs.keys()
334
335
def getPlayerID():
336
    return db.playerID
337
338
def getPlayer():
339
    return db[db.playerID]
340
341 View Code Duplication
def getDiplomacyWith(contactID):
0 ignored issues
show
This code seems to be duplicated in your project.
Loading history...
342
    obj = getPlayer()
343
    dipl = obj.diplomacyRels.get(contactID, None)
344
    if not dipl:
345
        # make default
346
        dipl = IDataHolder()
347
        dipl.type = Const.T_DIPLREL
348
        dipl.pacts = {
349
            Const.PACT_ALLOW_CIVILIAN_SHIPS: [Const.PACT_ACTIVE, Const.PACT_ALLOW_CIVILIAN_SHIPS]
350
        }
351
        dipl.relation = obj.defaultRelation
352
        dipl.relChng = 0
353
        dipl.lastContact = 0
354
        dipl.stats = None
355
        dipl.contactType = Const.CONTACT_NONE
356
        obj.diplomacyRels[playerID] = dipl
357
    return dipl
358
359
def getMessages():
360
    # construct list of mailboxes
361
    mailboxes = []
362
    mailboxes.append((db.playerID, getMessagesLastID(db.playerID)))
363
    galaxyID = getPlayer().galaxy
364
    if galaxyID:
365
        mailboxes.append((galaxyID, getMessagesLastID(galaxyID)))
366
    mailboxes.append((Const.OID_UNIVERSE, getMessagesLastID(Const.OID_UNIVERSE)))
367
    # get data
368
    data = cmdProxy.multiGetMsgs(Const.OID_UNIVERSE, mailboxes)
369
    # process
370
    new = 0
371
    now = time.time()
372
    for objID, messages in data:
373
        obj = get(objID)
374
        # delete old messages TODO leave this to the user
375
        #for messageID in obj._messages.keys():
376
        #    message = obj._messages[messageID]
377
        #    if message["time"] + Rules.messageTimeout < now:
378
        #        del obj._messages[messageID]
379
        # add new
380
        for message in messages:
381
            #@log.debug("Got message ID", message["id"])
382
            if message["id"] not in obj._messages:
383
                if message["forum"] != "OUTBOX":
384
                    message["readed"] = 0
385
                    message["replied"] = 0
386
                else:
387
                    message["readed"] = 1
388
                    message["replied"] = 0
389
                obj._messagesLastID = max(message["id"], obj._messagesLastID)
390
                obj._messages[message["id"]] = message
391
                new += 1
392
            else:
393
                log.warning("Got duplicated message", message)
394
    if new > 0:
395
        callbackObj.onNewMessages(new)
396
    return new
397
398
def getMessagesLastID(objID):
399
    obj = get(objID, publicOnly = 1)
400
    if not hasattr(obj, "_messages"):
401
        log.debug("Creating _messages")
402
        obj._messages = {}
403
        obj._messagesLastID = -1
404
    return obj._messagesLastID
405