/
savegameaccessor.py
238 lines (186 loc) · 8.39 KB
/
savegameaccessor.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
# ###################################################
# Copyright (C) 2011 The Unknown Horizons Team
# team@unknown-horizons.org
# This file is part of Unknown Horizons.
#
# Unknown Horizons 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 of the License, 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
# ###################################################
from collections import defaultdict, deque
from horizons.savegamemanager import SavegameManager
from horizons.util import DbReader
########################################################################
class SavegameAccessor(DbReader):
"""SavegameAccessor is the class used for loading saved games.
Frequent select queries are preloaded for faster access."""
def __init__(self, dbfile):
super(SavegameAccessor, self).__init__(dbfile=dbfile)
self._upgrade_savegame(dbfile)
self._load_building()
self._load_settlement()
self._load_concrete_object()
self._load_production()
self._load_storage()
self._load_storage_slot_limit()
self._load_wildanimal()
self._load_unit()
self._load_building_collector()
self._load_production_line()
self._load_unit_path()
self._load_component()
self._load_storage_global_limit()
def _upgrade_savegame(self, dbfile):
"""Tries to make old savegames compatible with the current version"""
metadata = SavegameManager.get_metadata(dbfile)
rev = metadata['savegamerev']
if rev == 0: # not a regular savegame, usually a map
return
if rev <= 44:
# add trade history table
self("CREATE TABLE \"trade_history\" (\"settlement\" INTEGER NOT NULL," \
"\"tick\" INTEGER NOT NULL, \"player\" INTEGER NOT NULL, " \
"\"resource_id\" INTEGER NOT NULL, \"amount\" INTEGER NOT NULL, \"gold\" INTEGER NOT NULL)")
def _load_building(self):
self._building = {}
for row in self("SELECT rowid, x, y, location, rotation, level FROM building"):
self._building[int(row[0])] = row[1:]
def get_building_row(self, worldid):
"""Returns (x, y, location, rotation, level)"""
return self._building[int(worldid)]
def get_building_location(self, worldid):
return self._building[int(worldid)][2]
def _load_settlement(self):
self._settlement = {}
for row in self("SELECT rowid, owner, island FROM settlement"):
self._settlement[int(row[0])] = row[1:]
def get_settlement_owner(self, worldid):
"""Returns the id of the owner of the settlement or None otherwise"""
worldid = int(worldid)
return None if worldid not in self._settlement else self._settlement[worldid][0]
def get_settlement_island(self, worldid):
return self._settlement[int(worldid)][1]
def _load_concrete_object(self):
self._concrete_object = {}
for row in self("SELECT id, action_runtime FROM concrete_object"):
self._concrete_object[int(row[0])] = int(row[1])
def get_concrete_object_action_runtime(self, worldid):
return self._concrete_object[int(worldid)]
def _load_production(self):
self._production = {}
self._production_ids = {}
db_data = self("SELECT rowid, state, owner, prod_line_id, remaining_ticks, _pause_old_state, creation_tick FROM production")
for row in db_data:
rowid = int(row[0])
self._production[rowid] = row[1:]
owner = int(row[2])
if owner in self._production_ids:
self._production_ids[owner].append(rowid)
else:
self._production_ids[owner] = [rowid]
self._production_state_history = defaultdict(lambda: deque())
for production_id, tick, state in self("SELECT production, tick, state FROM production_state_history ORDER BY production, tick"):
self._production_state_history[int(production_id)].append((tick, state))
def get_production_row(self, worldid):
"""Returns (state, owner, prod_line_id, remaining_ticks, _pause_old_state, creation_tick)"""
return self._production[int(worldid)]
def get_production_ids_by_owner(self, ownerid):
"""Returns potentially empty list of worldids referencing productions"""
ownerid = int(ownerid)
return [] if ownerid not in self._production_ids else self._production_ids[ownerid]
def get_production_line_id(self, production_worldid):
"""Returns the prod_line_id of the given production"""
return self._production[int(production_worldid)][2]
def get_production_state_history(self, worldid):
return self._production_state_history[int(worldid)]
def _load_storage(self):
self._storage = {}
for row in self("SELECT object, resource, amount FROM storage"):
ownerid = int(row[0])
if ownerid in self._storage:
self._storage[ownerid].append(row[1:])
else:
self._storage[ownerid] = [row[1:]]
def get_storage_rowids_by_ownerid(self, ownerid):
"""Returns potentially empty list of worldids referencing storages"""
ownerid = int(ownerid)
return [] if ownerid not in self._storage else self._storage[ownerid]
def _load_storage_slot_limit(self):
self._storage_slot_limit = {}
for row in self("SELECT object, slot, value FROM storage_slot_limit"):
key = (int(row[0]), int(row[1]))
self._storage_slot_limit[key] = int(row[2])
def get_storage_slot_limit(self, ownerid, slot):
return self._storage_slot_limit[(int(ownerid), int(slot))]
def _load_wildanimal(self):
self._wildanimal = {}
for row in self("SELECT rowid, health, can_reproduce FROM wildanimal"):
self._wildanimal[int(row[0])] = row[1:]
def get_wildanimal_row(self, worldid):
"""Returns (health, can_reproduce)"""
return self._wildanimal[int(worldid)]
def _load_unit(self):
self._unit = {}
for row in self("SELECT rowid, owner FROM unit"):
self._unit[int(row[0])] = int(row[1])
def get_unit_owner(self, worldid):
return self._unit[int(worldid)]
def _load_building_collector(self):
self._building_collector = {}
for row in self("SELECT rowid, home_building, creation_tick FROM building_collector"):
self._building_collector[int(row[0])] = (int(row[1]) if row[1] is not None else None, row[2])
self._building_collector_job_history = defaultdict(lambda: deque())
for collector_id, tick, utilisation in self("SELECT collector, tick, utilisation FROM building_collector_job_history ORDER BY collector, tick"):
self._building_collector_job_history[int(collector_id)].append((tick, utilisation))
def get_building_collectors_data(self, worldid):
"""Returns (id of the building collector's home or None otherwise, creation_tick)"""
worldid = int(worldid)
return None if worldid not in self._building_collector else self._building_collector[worldid]
def get_building_collector_job_history(self, worldid):
return self._building_collector_job_history[int(worldid)]
def _load_production_line(self):
self._production_line = {}
for row in self("SELECT for_worldid, type, res, amount FROM production_line"):
id = int(row[0])
if id not in self._production_line:
self._production_line[id] = []
self._production_line[id].append(row[1:])
def get_production_line_row(self, for_worldid):
return self._production_line[int(for_worldid)]
def _load_unit_path(self):
self._unit_path = {}
for row in self("SELECT unit, x, y FROM unit_path ORDER BY 'index'"):
id = int(row[0])
if id not in self._unit_path:
self._unit_path[id] = []
self._unit_path[id].append(row[1:])
def get_unit_path(self, worldid):
worldid = int(worldid)
return self._unit_path[worldid] if worldid in self._unit_path else None
def _load_component(self):
self._component = {}
for row in self("SELECT worldid, name, module, class FROM component"):
id = int(row[0])
if id not in self._component:
self._component[id] = []
self._component[id].append(row[1:])
def get_component_row(self, worldid):
worldid = int(worldid)
return self._component[worldid] if worldid in self._component else []
def _load_storage_global_limit(self):
self._storage_global_limit = {}
for row in self("SELECT object, value FROM storage_global_limit"):
self._storage_global_limit[(int(row[0]))] = int(row[1])
def get_storage_global_limit(self, worldid):
return self._storage_global_limit[int(worldid)]