genmap.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. #!/usr/bin/python2
  2. # This is an example script that generates some valid map data.
  3. import struct
  4. import random
  5. import os
  6. import sys
  7. import zlib
  8. import array
  9. from pnoise import pnoise
  10. # Old directory format:
  11. # world/sectors/XXXXZZZZ/YYYY
  12. # XXXX,YYYY,ZZZZ = coordinates in hexadecimal
  13. # fffe = -2
  14. # ffff = -1
  15. # 0000 = 0
  16. # 0001 = 1
  17. #
  18. # New directory format:
  19. # world/sectors2/XXX/ZZZ/YYYY
  20. # XXX,YYYY,ZZZ = coordinates in hexadecimal
  21. # fffe = -2
  22. # ffff = -1
  23. # 0000 = 0
  24. # 0001 = 1
  25. # ffe = -2
  26. # fff = -1
  27. # 000 = 0
  28. # 001 = 1
  29. #
  30. # For more proper file format documentation, refer to mapformat.txt
  31. # For node type documentation, refer to mapnode.h
  32. # NodeMetadata documentation is not complete, refer to nodemeta.cpp
  33. #
  34. # Seed for generating terrain
  35. SEED = 0
  36. # 0=old, 1=new
  37. SECTOR_DIR_FORMAT = 1
  38. mapdir = "../world"
  39. def to4h(i):
  40. s = "";
  41. s += '{0:1x}'.format((i>>12) & 0x000f)
  42. s += '{0:1x}'.format((i>>8) & 0x000f)
  43. s += '{0:1x}'.format((i>>4) & 0x000f)
  44. s += '{0:1x}'.format((i>>0) & 0x000f)
  45. return s
  46. def to3h(i):
  47. s = "";
  48. s += '{0:1x}'.format((i>>8) & 0x000f)
  49. s += '{0:1x}'.format((i>>4) & 0x000f)
  50. s += '{0:1x}'.format((i>>0) & 0x000f)
  51. return s
  52. def get_sector_dir(px, pz):
  53. global SECTOR_DIR_FORMAT
  54. if SECTOR_DIR_FORMAT == 0:
  55. return "/sectors/"+to4h(px)+to4h(pz)
  56. elif SECTOR_DIR_FORMAT == 1:
  57. return "/sectors2/"+to3h(px)+"/"+to3h(pz)
  58. else:
  59. assert(0)
  60. def getrand_air_stone():
  61. i = random.randrange(0,2)
  62. if i==0:
  63. return 0
  64. return 254
  65. # 3-dimensional vector (position)
  66. class v3:
  67. def __init__(self, x=0, y=0, z=0):
  68. self.X = x
  69. self.Y = y
  70. self.Z = z
  71. class NodeMeta:
  72. def __init__(self, type_id, data):
  73. self.type_id = type_id
  74. self.data = data
  75. class StaticObject:
  76. def __init__(self):
  77. self.type_id = 0
  78. self.data = ""
  79. def ser_u16(i):
  80. return chr((i>>8)&0xff) + chr((i>>0)&0xff)
  81. def ser_u32(i):
  82. return (chr((i>>24)&0xff) + chr((i>>16)&0xff)
  83. + chr((i>>8)&0xff) + chr((i>>0)&0xff))
  84. # A 16x16x16 chunk of map
  85. class MapBlock:
  86. def __init__(self):
  87. self.content = array.array('B')
  88. self.param1 = array.array('B')
  89. self.param2 = array.array('B')
  90. for i in range(16*16*16):
  91. # Initialize to air
  92. self.content.append(254)
  93. # Full light on sunlight, none when no sunlight
  94. self.param1.append(15)
  95. # No additional parameters
  96. self.param2.append(0)
  97. # key = v3 pos
  98. # value = NodeMeta
  99. self.nodemeta = {}
  100. # key = v3 pos
  101. # value = StaticObject
  102. self.static_objects = {}
  103. def set_content(self, v3, b):
  104. self.content[v3.Z*16*16+v3.Y*16+v3.X] = b
  105. def set_param1(self, v3, b):
  106. self.param1[v3.Z*16*16+v3.Y*16+v3.X] = b
  107. def set_param2(self, v3, b):
  108. self.param2[v3.Z*16*16+v3.Y*16+v3.X] = b
  109. # Get data for serialization. Returns a string.
  110. def serialize_data(self):
  111. s = ""
  112. for i in range(16*16*16):
  113. s += chr(self.content[i])
  114. for i in range(16*16*16):
  115. s += chr(self.param1[i])
  116. for i in range(16*16*16):
  117. s += chr(self.param2[i])
  118. return s
  119. def serialize_nodemeta(self):
  120. s = ""
  121. s += ser_u16(1)
  122. s += ser_u16(len(self.nodemeta))
  123. for pos, meta in self.nodemeta.items():
  124. pos_i = pos.Z*16*16 + pos.Y*16 + pos.X
  125. s += ser_u16(pos_i)
  126. s += ser_u16(meta.type_id)
  127. s += ser_u16(len(meta.data))
  128. s += meta.data
  129. return s
  130. def serialize_staticobj(self):
  131. s = ""
  132. s += chr(0)
  133. s += ser_u16(len(self.static_objects))
  134. for pos, obj in self.static_objects.items():
  135. pos_i = pos.Z*16*16 + pos.Y*16 + pos.X
  136. s += ser_s32(pos.X*1000)
  137. s += ser_s32(pos.Y*1000)
  138. s += ser_s32(pos.Z*1000)
  139. s += ser_u16(obj.type_id)
  140. s += ser_u16(len(obj.data))
  141. s += obj.data
  142. return s
  143. def writeblock(mapdir, px,py,pz, block):
  144. sectordir = mapdir + get_sector_dir(px, pz);
  145. try:
  146. os.makedirs(sectordir)
  147. except OSError:
  148. pass
  149. path = sectordir+"/"+to4h(py)
  150. print("writing block file "+path)
  151. f = open(sectordir+"/"+to4h(py), "wb")
  152. if f == None:
  153. return
  154. # version
  155. version = 17
  156. f.write(struct.pack('B', version))
  157. # flags
  158. # 0x01=is_undg, 0x02=dn_diff, 0x04=lighting_expired
  159. flags = 0 + 0x02 + 0x04
  160. f.write(struct.pack('B', flags))
  161. # data
  162. c_obj = zlib.compressobj()
  163. c_obj.compress(block.serialize_data())
  164. f.write(struct.pack('BB', 0x78, 0x9c)) # zlib magic number
  165. f.write(c_obj.flush())
  166. # node metadata
  167. c_obj = zlib.compressobj()
  168. c_obj.compress(block.serialize_nodemeta())
  169. f.write(struct.pack('BB', 0x78, 0x9c)) # zlib magic number
  170. f.write(c_obj.flush())
  171. # mapblockobject count
  172. f.write(ser_u16(0))
  173. # static objects
  174. f.write(block.serialize_staticobj())
  175. # timestamp
  176. f.write(ser_u32(0xffffffff))
  177. f.close()
  178. for z0 in range(-1,3):
  179. for x0 in range(-1,3):
  180. for y0 in range(-1,3):
  181. print("generating block "+str(x0)+","+str(y0)+","+str(z0))
  182. #v3 blockp = v3(x0,y0,z0)
  183. # Create a MapBlock
  184. block = MapBlock()
  185. # Generate stuff in it
  186. for z in range(0,16):
  187. for x in range(0,16):
  188. h = 20.0*pnoise((x0*16+x)/100.,(z0*16+z)/100.,SEED+0)
  189. h += 5.0*pnoise((x0*16+x)/25.,(z0*16+z)/25.,SEED+0)
  190. if pnoise((x0*16+x)/25.,(z0*16+z)/25.,SEED+92412) > 0.05:
  191. h += 10
  192. #print("r="+str(r))
  193. # This enables comparison by ==
  194. h = int(h)
  195. for y in range(0,16):
  196. p = v3(x,y,z)
  197. b = 254
  198. y1 = y0*16+y
  199. if y1 <= h-3:
  200. b = 0 #stone
  201. elif y1 <= h and y1 <= 0:
  202. b = 8 #mud
  203. elif y1 == h:
  204. b = 1 #grass
  205. elif y1 < h:
  206. b = 8 #mud
  207. elif y1 <= 1:
  208. b = 9 #water
  209. # Material content
  210. block.set_content(p, b)
  211. # Place a sign at the center at surface level.
  212. # Placing a sign means placing the sign node and
  213. # adding node metadata to the mapblock.
  214. if x == 8 and z == 8 and y0*16 <= h-1 and (y0+1)*16-1 > h:
  215. p = v3(8,h+1-y0*16,8)
  216. # 14 = Sign
  217. content_type = 14
  218. block.set_content(p, content_type)
  219. # This places the sign to the bottom of the cube.
  220. # Working values: 0x01, 0x02, 0x04, 0x08, 0x10, 0x20
  221. block.set_param2(p, 0x08)
  222. # Then add metadata to hold the text of the sign
  223. s = "Hello at sector ("+str(x0)+","+str(z0)+")"
  224. meta = NodeMeta(content_type, ser_u16(len(s))+s)
  225. block.nodemeta[p] = meta
  226. # Write it on disk
  227. writeblock(mapdir, x0,y0,z0, block)
  228. #END