forked from boskee/Minecraft
-
Notifications
You must be signed in to change notification settings - Fork 2
/
savingsystem.py
182 lines (157 loc) · 7.17 KB
/
savingsystem.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
# Imports, sorted alphabetically.
# Python packages
import cPickle as pickle
import os
import random
import struct
import time
# Third-party packages
# Nothing for now...
# Modules from this project
from blocks import BlockID
from debug import performance_info
import globals as G
from player import Player
__all__ = (
'sector_to_filename', 'region_to_filename', 'sector_to_region',
'sector_to_offset', 'save_world', 'world_exists', 'remove_world',
'sector_exists', 'load_region', 'open_world',
)
structvec = struct.Struct("hhh")
structushort = struct.Struct("H")
structuchar2 = struct.Struct("BB")
structvecBB = struct.Struct("hhhBB")
null2 = struct.pack("xx") #Two \0's
null1024 = null2*512 #1024 \0's
air = G.BLOCKS_DIR[(0,0)]
def sector_to_filename(secpos):
x,y,z = secpos
return "%i.%i.%i.pyr" % (x/4, y/4, z/4)
def region_to_filename(region):
return "%i.%i.%i.pyr" % region
def sector_to_region(secpos):
x,y,z = secpos
return (x/4, y/4, z/4)
def sector_to_offset(secpos):
x,y,z = secpos
return ((x % 4)*16 + (y % 4)*4 + (z % 4)) * 1024
def sector_to_blockpos(secpos):
x,y,z = secpos
return x*8, y*8, z*8
def save_sector_to_string(blocks, secpos):
cx, cy, cz = sector_to_blockpos(secpos)
fstr = ""
for x in xrange(cx, cx+8):
for y in xrange(cy, cy+8):
for z in xrange(cz, cz+8):
blk = blocks.get((x,y,z), air).id
if blk is not air:
#if isinstance(blk, int): # When does this occur? Its expensive and I don't see it triggering
# blk = BlockID(blk)
fstr += structuchar2.pack(blk.main, blk.sub)
else:
fstr += null2
return fstr
def save_world(server, world):
#Non block related data
#save = (4,window.player, window.time_of_day, G.SEED)
#pickle.dump(save, open(os.path.join(game_dir, world, "save.pkl"), "wb"))
for player in server.players:
save_player(player, world)
save_blocks(server.world, world)
def save_blocks(blocks, world):
#blocks and sectors (window.world and window.world.sectors)
#Saves individual sectors in region files (4x4x4 sectors)
for secpos in blocks.sectors: #TODO: only save dirty sectors
if not blocks.sectors[secpos]:
continue #Skip writing empty sectors
file = os.path.join(G.game_dir, world, sector_to_filename(secpos))
if not os.path.exists(file):
with open(file, "w") as f:
f.truncate(64*1024) #Preallocate the file to be 64kb
with open(file, "rb+") as f: #Load up the region file
f.seek(sector_to_offset(secpos)) #Seek to the sector offset
f.write(save_sector_to_string(blocks, secpos))
def save_player(player, world):
if type(player) == tuple:
print player
with open(os.path.join(G.game_dir, world, "players", player.username+".dat"), "wb") as f:
f.write("\1"+player.inventory)
#TODO: Save player position (and rotation?)
def world_exists(game_dir, world=None):
if world is None: world = "world"
return os.path.lexists(os.path.join(game_dir, world))
def remove_world(game_dir, world=None):
if world is None: world = "world"
if world_exists(game_dir, world):
import shutil
shutil.rmtree(os.path.join(game_dir, world))
def sector_exists(sector, world=None):
if world is None: world = "world"
return os.path.lexists(os.path.join(G.game_dir, world, sector_to_filename(sector)))
def load_region(world, world_name=None, region=None, sector=None):
if world_name is None: world_name = "world"
sectors = world.sectors
blocks = world
SECTOR_SIZE = G.SECTOR_SIZE
BLOCKS_DIR = G.BLOCKS_DIR
if sector: region = sector_to_region(sector)
rx,ry,rz = region
rx,ry,rz = rx*32, ry*32, rz*32
with open(os.path.join(G.game_dir, world_name, region_to_filename(region)), "rb") as f:
#Load every chunk in this region (4x4x4)
for cx in xrange(rx, rx+32, 8):
for cy in xrange(ry, ry+32, 8):
for cz in xrange(rz, rz+32, 8):
#Now load every block in this chunk (8x8x8)
fstr = f.read(1024)
if fstr != null1024:
fpos = 0
for x in xrange(cx, cx+8):
for y in xrange(cy, cy+8):
for z in xrange(cz, cz+8):
read = fstr[fpos:fpos+2]
fpos += 2
if read != null2:
position = x,y,z
try:
full_id = structuchar2.unpack(read)
blocks[position] = BLOCKS_DIR[full_id]
if blocks[position].sub_id_as_metadata:
blocks[position] = type(BLOCKS_DIR[full_id])()
blocks[position].set_metadata(full_id[-1])
except KeyError:
try:
main_blk = BLOCKS_DIR[(full_id[0], 0)]
if main_blk.sub_id_as_metadata: # sub id is metadata
blocks[position] = type(main_blk)()
blocks[position].set_metadata(full_id[-1])
except KeyError as e:
print "load_region: Invalid Block", e
sectors[(x/SECTOR_SIZE, y/SECTOR_SIZE, z/SECTOR_SIZE)].append(position)
def load_player(player, world):
if os.path.lexists(os.path.join(G.game_dir, world, "players", player.username+".dat")):
with open(os.path.join(G.game_dir, world, "players", player.username+".dat"), "rb") as f:
version = struct.unpack("B", f.read(1))[0]
if version == 1:
player.inventory = f.read(4*40)
@performance_info
def open_world(gamecontroller, game_dir, world=None):
if world is None: world = "world"
#Non block related data
loaded_save = pickle.load(open(os.path.join(game_dir, world, "save.pkl"), "rb"))
if loaded_save[0] == 4:
if isinstance(loaded_save[1], Player): gamecontroller.player = loaded_save[1]
if isinstance(loaded_save[2], float): gamecontroller.time_of_day = loaded_save[2]
if isinstance(loaded_save[3], str):
G.SEED = loaded_save[3]
random.seed(G.SEED)
print('Loaded seed from save: ' + G.SEED)
elif loaded_save[0] == 3: #Version 3
if isinstance(loaded_save[1], Player): gamecontroller.player = loaded_save[1]
if isinstance(loaded_save[2], float): gamecontroller.time_of_day = loaded_save[2]
G.SEED = str(long(time.time() * 256))
random.seed(G.SEED)
print('No seed in save, generated random seed: ' + G.SEED)
#blocks and sectors (window.world and window.world.sectors)
#Are loaded on the fly