You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

428 lines
14 KiB

  1. SUBSYSTEM_DEF(demo)
  2. name = "Demo"
  3. wait = 1
  4. flags = SS_TICKER
  5. init_order = INIT_ORDER_DEMO
  6. runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
  7. var/list/pre_init_lines = list() // stuff like chat before the init
  8. var/list/icon_cache = list()
  9. var/list/icon_state_caches = list()
  10. var/list/name_cache = list()
  11. var/list/marked_dirty = list()
  12. var/list/marked_new = list()
  13. var/list/marked_turfs = list()
  14. var/list/del_list = list()
  15. var/last_written_time = null
  16. var/last_chat_message = null
  17. // stats stuff
  18. var/last_queued = 0
  19. var/last_completed = 0
  20. /datum/controller/subsystem/demo/proc/write_time()
  21. var/new_time = world.time
  22. if(last_written_time != new_time)
  23. if(initialized)
  24. WRITE_LOG_NO_FORMAT(GLOB.demo_log, "time [new_time]\n")
  25. else
  26. pre_init_lines += "time [new_time]"
  27. last_written_time = new_time
  28. /datum/controller/subsystem/demo/proc/write_event_line(line)
  29. write_time()
  30. if(initialized)
  31. WRITE_LOG_NO_FORMAT(GLOB.demo_log, "[line]\n")
  32. else
  33. pre_init_lines += line
  34. /datum/controller/subsystem/demo/proc/write_chat(target, text)
  35. var/target_text = ""
  36. if(target == GLOB.clients)
  37. target_text = "world"
  38. else if(islist(target))
  39. var/list/target_keys = list()
  40. for(var/T in target)
  41. var/client/C = CLIENT_FROM_VAR(T)
  42. if(C)
  43. target_keys += C.ckey
  44. if(!target_keys.len)
  45. return
  46. target_text = jointext(target_keys, ",")
  47. else
  48. var/client/C = CLIENT_FROM_VAR(target)
  49. if(C)
  50. target_text = C.ckey
  51. else
  52. return
  53. write_event_line("chat [target_text] [last_chat_message == text ? "=" : json_encode(text)]")
  54. last_chat_message = text
  55. /datum/controller/subsystem/demo/Initialize()
  56. WRITE_LOG_NO_FORMAT(GLOB.demo_log, "demo version 1\n") // increment this if you change the format
  57. if(GLOB.revdata)
  58. WRITE_LOG_NO_FORMAT(GLOB.demo_log, "commit [GLOB.revdata.originmastercommit || GLOB.revdata.commit]\n")
  59. // write a "snapshot" of the world at this point.
  60. // start with turfs
  61. log_world("Writing turfs...")
  62. WRITE_LOG_NO_FORMAT(GLOB.demo_log, "init [world.maxx] [world.maxy] [world.maxz]\n")
  63. marked_turfs.Cut()
  64. for(var/z in 1 to world.maxz)
  65. var/row_list = list()
  66. var/last_appearance
  67. var/rle_count = 1
  68. for(var/y in 1 to world.maxy)
  69. for(var/x in 1 to world.maxx)
  70. var/turf/T = locate(x,y,z)
  71. T.demo_last_appearance = T.appearance
  72. var/this_appearance
  73. // space turfs are difficult to RLE otherwise, because they all
  74. // have different appearances despite being the same thing.
  75. if(T.type == /turf/open/space || T.type == /turf/open/space/basic)
  76. this_appearance = "s" // save the bytes
  77. else if(istype(T, /turf/open/space/transit))
  78. this_appearance = "t[T.dir]"
  79. else
  80. this_appearance = T.appearance
  81. if(this_appearance == last_appearance)
  82. rle_count++
  83. else
  84. if(rle_count > 1)
  85. row_list += rle_count
  86. rle_count = 1
  87. if(istext(this_appearance))
  88. row_list += this_appearance
  89. else
  90. // do a diff with the previous turf to save those bytes
  91. row_list += encode_appearance(this_appearance, istext(last_appearance) ? null : last_appearance)
  92. last_appearance = this_appearance
  93. if(rle_count > 1)
  94. row_list += rle_count
  95. WRITE_LOG_NO_FORMAT(GLOB.demo_log, jointext(row_list, ",") + "\n")
  96. CHECK_TICK
  97. // then do objects
  98. log_world("Writing objects")
  99. marked_new.Cut()
  100. marked_dirty.Cut()
  101. for(var/z in 1 to world.maxz)
  102. var/spacing = 0
  103. var/row_list = list()
  104. for(var/y in 1 to world.maxy)
  105. for(var/x in 1 to world.maxx)
  106. var/turf/T = locate(x,y,z)
  107. var/list/turf_list = list()
  108. for(var/C in T.contents)
  109. var/atom/movable/as_movable = C
  110. if(as_movable.loc != T)
  111. continue
  112. if(isobj(C) || ismob(C))
  113. turf_list += encode_init_obj(C)
  114. if(turf_list.len)
  115. if(spacing)
  116. row_list += spacing
  117. spacing = 0
  118. row_list += turf_list
  119. spacing++
  120. CHECK_TICK // This is a bit risky because something might change but meh, its not a big deal.
  121. WRITE_LOG_NO_FORMAT(GLOB.demo_log, jointext(row_list, ",") + "\n")
  122. // track objects that exist in nullspace
  123. var/nullspace_list = list()
  124. for(var/M in world)
  125. if(!isobj(M) && !ismob(M))
  126. continue
  127. var/atom/movable/AM = M
  128. if(AM.loc != null)
  129. continue
  130. nullspace_list += encode_init_obj(AM)
  131. CHECK_TICK
  132. WRITE_LOG_NO_FORMAT(GLOB.demo_log, jointext(nullspace_list, ",") + "\n")
  133. for(var/line in pre_init_lines)
  134. WRITE_LOG_NO_FORMAT(GLOB.demo_log, "[line]\n")
  135. return ..()
  136. /datum/controller/subsystem/demo/fire()
  137. if(!src.marked_new.len && !src.marked_dirty.len && !src.marked_turfs.len && !src.del_list.len)
  138. return // nothing to do
  139. last_queued = src.marked_new.len + src.marked_dirty.len + src.marked_turfs.len
  140. last_completed = 0
  141. write_time()
  142. if(src.del_list.len)
  143. var/s = "del [jointext(src.del_list, ",")]\n" // if I don't do it like this I get "incorrect number of macro arguments" because byond is stupid and sucks
  144. WRITE_LOG_NO_FORMAT(GLOB.demo_log, s)
  145. src.del_list.Cut()
  146. var/canceled = FALSE
  147. var/list/marked_dirty = src.marked_dirty
  148. var/list/dirty_updates = list()
  149. while(marked_dirty.len)
  150. last_completed++
  151. var/atom/movable/M = marked_dirty[marked_dirty.len]
  152. marked_dirty.len--
  153. if(M.gc_destroyed || !M)
  154. continue
  155. if(M.loc == M.demo_last_loc && M.appearance == M.demo_last_appearance)
  156. continue
  157. var/loc_string = "="
  158. if(M.loc != M.demo_last_loc)
  159. loc_string = "null"
  160. if(isturf(M.loc))
  161. loc_string = "[M.x],[M.y],[M.z]"
  162. else if(ismovable(M.loc))
  163. loc_string = "\ref[M.loc]"
  164. M.demo_last_loc = M.loc
  165. var/appearance_string = "="
  166. if(M.appearance != M.demo_last_appearance)
  167. appearance_string = encode_appearance(M.appearance, M.demo_last_appearance)
  168. M.demo_last_appearance = M.appearance
  169. dirty_updates += "\ref[M] [loc_string] [appearance_string]"
  170. if(MC_TICK_CHECK)
  171. canceled = TRUE
  172. break
  173. if(dirty_updates.len)
  174. var/s = "update [jointext(dirty_updates, ",")]\n"
  175. WRITE_LOG_NO_FORMAT(GLOB.demo_log, s)
  176. if(canceled)
  177. return
  178. var/list/marked_new = src.marked_new
  179. var/list/new_updates = list()
  180. while(marked_new.len)
  181. last_completed++
  182. var/atom/movable/M = marked_new[marked_new.len]
  183. marked_new.len--
  184. if(M.gc_destroyed || !M)
  185. continue
  186. var/loc_string = "null"
  187. if(isturf(M.loc))
  188. loc_string = "[M.x],[M.y],[M.z]"
  189. else if(ismovable(M.loc))
  190. loc_string = "\ref[M.loc]"
  191. M.demo_last_appearance = M.appearance
  192. new_updates += "\ref[M] [loc_string] [encode_appearance(M.appearance)]"
  193. if(MC_TICK_CHECK)
  194. canceled = TRUE
  195. break
  196. if(new_updates.len)
  197. var/s = "new [jointext(new_updates, ",")]\n"
  198. WRITE_LOG_NO_FORMAT(GLOB.demo_log, s)
  199. if(canceled)
  200. return
  201. var/list/marked_turfs = src.marked_turfs
  202. var/list/turf_updates = list()
  203. while(marked_turfs.len)
  204. last_completed++
  205. var/turf/T = marked_turfs[marked_turfs.len]
  206. marked_turfs.len--
  207. if(T && T.appearance != T.demo_last_appearance)
  208. turf_updates += "([T.x],[T.y],[T.z])=[encode_appearance(T.appearance, T.demo_last_appearance)]"
  209. T.demo_last_appearance = T.appearance
  210. if(MC_TICK_CHECK)
  211. canceled = TRUE
  212. break
  213. if(turf_updates.len)
  214. var/s = "turf [jointext(turf_updates, ",")]\n"
  215. WRITE_LOG_NO_FORMAT(GLOB.demo_log, s)
  216. if(canceled)
  217. return
  218. /datum/controller/subsystem/demo/proc/encode_init_obj(atom/movable/M)
  219. M.demo_last_loc = M.loc
  220. M.demo_last_appearance = M.appearance
  221. var/encoded_appearance = encode_appearance(M.appearance)
  222. var/list/encoded_contents = list()
  223. for(var/C in M.contents)
  224. if(isobj(C) || ismob(C))
  225. encoded_contents += encode_init_obj(C)
  226. return "\ref[M]=[encoded_appearance][(encoded_contents.len ? "([jointext(encoded_contents, ",")])" : "")]"
  227. // please make sure the order you call this function in is the same as the order you write
  228. /datum/controller/subsystem/demo/proc/encode_appearance(image/appearance, image/diff_appearance, diff_remove_overlays = FALSE)
  229. if(appearance == null)
  230. return "n"
  231. if(appearance == diff_appearance)
  232. return "="
  233. var/icon_txt = "[appearance.icon]"
  234. var/cached_icon = icon_cache[icon_txt] || icon_txt
  235. var/list/icon_state_cache
  236. if(!isnum(cached_icon))
  237. icon_cache[icon_txt] = icon_cache.len + 1
  238. icon_state_cache = (icon_state_caches[++icon_state_caches.len] = list())
  239. else
  240. icon_state_cache = icon_state_caches[cached_icon]
  241. var/list/cached_icon_state = icon_state_cache[appearance.icon_state] || appearance.icon_state
  242. if(!isnum(cached_icon_state))
  243. icon_state_cache[appearance.icon_state] = icon_state_cache.len + 1
  244. var/cached_name = name_cache[appearance.name] || appearance.name
  245. if(!isnum(cached_name))
  246. name_cache[appearance.name] = name_cache.len + 1
  247. var/color_string = appearance.color || "w"
  248. if(islist(color_string))
  249. var/list/old_list = appearance.color
  250. var/list/inted = list()
  251. inted.len = old_list.len
  252. for(var/i in 1 to old_list.len)
  253. inted[i] += round(old_list[i] * 255)
  254. color_string = jointext(inted, ",")
  255. var/overlays_string = "\[]"
  256. if(appearance.overlays.len)
  257. var/list/overlays_list = list()
  258. for(var/i in 1 to appearance.overlays.len)
  259. var/image/overlay = appearance.overlays[i]
  260. overlays_list += encode_appearance(overlay, appearance, TRUE)
  261. overlays_string = "\[[jointext(overlays_list, ",")]]"
  262. var/underlays_string = "\[]"
  263. if(appearance.underlays.len)
  264. var/list/underlays_list = list()
  265. for(var/i in 1 to appearance.underlays.len)
  266. var/image/underlay = appearance.underlays[i]
  267. underlays_list += encode_appearance(underlay, appearance, TRUE)
  268. underlays_string = "\[[jointext(underlays_list, ",")]]"
  269. var/appearance_transform_string = "i"
  270. if(appearance.transform)
  271. var/matrix/M = appearance.transform
  272. appearance_transform_string = "[M.a],[M.b],[M.c],[M.d],[M.e],[M.f]"
  273. if(appearance_transform_string == "1,0,0,0,1,0")
  274. appearance_transform_string = "i"
  275. var/list/appearance_list = list(
  276. json_encode(cached_icon),
  277. json_encode(cached_icon_state),
  278. json_encode(cached_name),
  279. appearance.appearance_flags,
  280. appearance.layer,
  281. appearance.plane == -32767 ? "" : appearance.plane,
  282. appearance.dir == 2 ? "" : appearance.dir,
  283. appearance.color ? color_string : "",
  284. appearance.alpha == 255 ? "" : appearance.alpha,
  285. appearance.pixel_x == 0 ? "" : appearance.pixel_x,
  286. appearance.pixel_y == 0 ? "" : appearance.pixel_y,
  287. appearance.blend_mode <= 1 ? "" : appearance.blend_mode,
  288. appearance_transform_string != "i" ? appearance_transform_string : "",
  289. appearance:invisibility == 0 ? "" : appearance:invisibility, // colon because dreamchecker is dumb
  290. appearance.pixel_w == 0 ? "" : appearance.pixel_w,
  291. appearance.pixel_z == 0 ? "" : appearance.pixel_z,
  292. appearance.overlays.len ? overlays_string : "",
  293. appearance.underlays.len ? underlays_string : ""
  294. )
  295. while(appearance_list[appearance_list.len] == "" && appearance_list.len > 0)
  296. appearance_list.len--
  297. var/undiffed_string = "{[jointext(appearance_list, ";")]}"
  298. if(diff_appearance)
  299. var/overlays_identical = TRUE
  300. if(diff_remove_overlays)
  301. overlays_identical = (appearance.overlays.len == 0)
  302. else if(appearance.overlays.len != diff_appearance.overlays.len)
  303. overlays_identical = FALSE
  304. else
  305. for(var/i in 1 to appearance.overlays.len)
  306. if(appearance.overlays[i] != diff_appearance.overlays[i])
  307. overlays_identical = FALSE
  308. break
  309. var/underlays_identical = TRUE
  310. if(diff_remove_overlays)
  311. underlays_identical = (appearance.underlays.len == 0)
  312. else if(appearance.underlays.len != diff_appearance.underlays.len)
  313. underlays_identical = FALSE
  314. else
  315. for(var/i in 1 to appearance.underlays.len)
  316. if(appearance.underlays[i] != diff_appearance.underlays[i])
  317. underlays_identical = FALSE
  318. break
  319. var/diff_transform_string = "i"
  320. if(diff_appearance.transform)
  321. var/matrix/M = diff_appearance.transform
  322. diff_transform_string = "[M.a],[M.b],[M.c],[M.d],[M.e],[M.f]"
  323. if(diff_transform_string == "1,0,0,0,1,0")
  324. diff_transform_string = "i"
  325. var/list/diffed_appearance_list = list(
  326. json_encode(cached_icon),
  327. json_encode(cached_icon_state),
  328. json_encode(cached_name),
  329. appearance.appearance_flags == diff_appearance.appearance_flags ? "" : appearance.appearance_flags,
  330. appearance.layer == diff_appearance.layer ? "" : appearance.layer,
  331. appearance.plane == diff_appearance.plane ? "" : appearance.plane,
  332. appearance.dir == diff_appearance.dir ? "" : appearance.dir,
  333. appearance.color == diff_appearance.color ? "" : color_string,
  334. appearance.alpha == diff_appearance.alpha ? "" : appearance.alpha,
  335. appearance.pixel_x == diff_appearance.pixel_x ? "" : appearance.pixel_x,
  336. appearance.pixel_y == diff_appearance.pixel_y ? "" : appearance.pixel_y,
  337. appearance.blend_mode == diff_appearance.blend_mode ? "" : appearance.blend_mode,
  338. appearance_transform_string == diff_transform_string ? "" : appearance_transform_string,
  339. appearance:invisibility == diff_appearance:invisibility ? "" : appearance:invisibility, // colon because dreamchecker is too dumb
  340. appearance.pixel_w == diff_appearance.pixel_w ? "" : appearance.pixel_w,
  341. appearance.pixel_z == diff_appearance.pixel_z ? "" : appearance.pixel_z,
  342. overlays_identical ? "" : overlays_string,
  343. underlays_identical ? "" :underlays_string
  344. )
  345. while(diffed_appearance_list[diffed_appearance_list.len] == "" && diffed_appearance_list.len > 0)
  346. diffed_appearance_list.len--
  347. var/diffed_string = "~{[jointext(diffed_appearance_list, ";")]}"
  348. if(length(diffed_string) < length(undiffed_string))
  349. return diffed_string
  350. return undiffed_string
  351. /datum/controller/subsystem/demo/stat_entry(msg)
  352. msg += "Remaining: {"
  353. msg += "Trf:[marked_turfs.len]|"
  354. msg += "New:[marked_new.len]|"
  355. msg += "Upd:[marked_dirty.len]|"
  356. msg += "Del:[del_list.len]"
  357. msg += "}"
  358. ..(msg)
  359. /datum/controller/subsystem/demo/proc/mark_turf(turf/T)
  360. if(!isturf(T))
  361. return
  362. marked_turfs[T] = TRUE
  363. /datum/controller/subsystem/demo/proc/mark_new(atom/movable/M)
  364. if(!isobj(M) && !ismob(M))
  365. return
  366. if(M.gc_destroyed)
  367. return
  368. marked_new[M] = TRUE
  369. if(marked_dirty[M])
  370. marked_dirty -= M
  371. // I can't wait for when TG ports this and they make this a #define macro.
  372. /datum/controller/subsystem/demo/proc/mark_dirty(atom/movable/M)
  373. if(!isobj(M) && !ismob(M))
  374. return
  375. if(M.gc_destroyed)
  376. return
  377. if(!marked_new[M])
  378. marked_dirty[M] = TRUE
  379. /datum/controller/subsystem/demo/proc/mark_destroyed(atom/movable/M)
  380. if(!isobj(M) && !ismob(M))
  381. return
  382. if(marked_new[M])
  383. marked_new -= M
  384. if(marked_dirty[M])
  385. marked_dirty -= M
  386. if(initialized)
  387. del_list["\ref[M]"] = 1