mirror of
https://github.com/goonstation/goonstation-2016.git
synced 2026-07-18 12:32:20 +01:00
583 lines
17 KiB
Plaintext
583 lines
17 KiB
Plaintext
/*
|
|
|
|
var_dump
|
|
|
|
Ported from PHP by Gazoot, March 2005.
|
|
http://developer.byond.com/people/Gazoot
|
|
|
|
What is it?
|
|
===========
|
|
var_dump is a flexible datum for dumping debug information. It handles pretty much everything,
|
|
from integers to savefiles and even self-referencing objects.
|
|
|
|
|
|
Quick start
|
|
===========
|
|
Run the demo to see the potential!
|
|
|
|
After that, there are four pre-defined procs for you to get started straight away.
|
|
Just put one of those in your code and you're set:
|
|
|
|
var_dump(var1, var2, ...) Dumps information about variables to the output window.
|
|
var_dump_s(var1, var2, ...) Dumps information, but only one level deep. Useful for quick debugs.
|
|
browser_dump(var1, var2, ...) Just like var_dump(), but outputs to the browser window.
|
|
browser_dump_s(var1, var2, ...) Just like var_dump_s(), but outputs to the browser window.
|
|
|
|
|
|
Customizing the datum
|
|
=====================
|
|
So you want more control over the output? No problem, you can have it. The demo has a verb called "custom_dump"
|
|
which will give you source code for different output formats. But here's how to do it manually:
|
|
|
|
1) Create a var_dump object. There are a couple to choose from:
|
|
|
|
var_dump/xhtml output is XHTML 1.0 strict.
|
|
var_dump/html output is html, suitable for the byond output window.
|
|
var_dump/text output is plain text
|
|
|
|
2) The constructor takes two arguments, depth and brief. (The brief type is only defined for convenience.)
|
|
|
|
var/var_dump/xhtml/B = new(depth, brief)
|
|
|
|
depth Is how many levels down in the datums/lists to go before stopping. A mob might reference an obj, which
|
|
references a list of turfs, which references many other objects. The default is 4, which should be
|
|
more than enough. 0 is infinite depth, which can take a long time to process.
|
|
(Sidenote: var_dump_s() uses 1 for depth.)
|
|
|
|
brief If set to 1, make a very brief dump. It skips datum vars completely. Useful for a quick overview
|
|
of a more complex system like a big savefile.
|
|
|
|
3) Call the Dump() or ListDump() function to create the output.
|
|
|
|
var/output = B.Dump(var1, var2, ...)
|
|
|
|
Or if you have your vars in a list:
|
|
|
|
var
|
|
mob/M = new
|
|
list/L = list(M.name, M.loc)
|
|
output = B.ListDump(L)
|
|
|
|
4) Do with the output whatever you want.
|
|
|
|
world << browse(output) // Let the whole world browse my data.
|
|
text2file(output, "dump.txt") // Save it to a file for later examination
|
|
|
|
5) Put the above code in your own proc.
|
|
|
|
When you are satisfied with the behaviour, make a proc with the above features, give it
|
|
a short name, and you are ready to dump! Look at the pre-defined verbs starting on line 171
|
|
in this file for examples how to do it.
|
|
|
|
|
|
Next step up
|
|
============
|
|
For those really tough bugs, Deadrons Debugger could be your helping hand. It can be downloaded at:
|
|
|
|
http://developer.byond.com/hub/Deadron/Debug
|
|
|
|
|
|
Anything else?
|
|
==============
|
|
There is one more thing to know, when dumping a datum, the "vars" list won't be dumped. The reason is
|
|
that vars is a list of the datum vars, which is used by var_dump to dump the datum! Even though self-
|
|
referencing checks are made (vars["vars"] = vars), there's no point displaying the exact same
|
|
information again.
|
|
|
|
|
|
Bugs?
|
|
=====
|
|
So you found a bug? Impossible! Just kidding, please submit them at the forum:
|
|
|
|
http://developer.byond.com/forums/Gazoot/var_dump/
|
|
|
|
Suggestions and ideas for improving var_dump are also welcome at the forum!
|
|
|
|
Thanks for using var_dump!
|
|
|
|
/Gazoot
|
|
|
|
|
|
Version history
|
|
===============
|
|
|
|
Version 13
|
|
* Added a var, "deep_list_length" to the var_dump datum, which forces brief output of
|
|
datums in a list longer than that length.
|
|
|
|
Version 12
|
|
* Removed the brief datum. It is better to specify it as an argument in New() instead.
|
|
* Rewrote the output functions for better handling of text.
|
|
* Fixed some depth var bugs.
|
|
|
|
Version 11
|
|
* XHTML broke the output window display. There is now a var_dump/xhtml datum which should be
|
|
used when output should be to the browser. For normal window output, use var_dump/html.
|
|
* Updated the predefined procs to use the new xhtml datum.
|
|
* Updated the demo to include XHTML as well.
|
|
|
|
Version 10
|
|
* Html output is now valid XHTML 1.0 strict.
|
|
* Fixed a typo in documentation.
|
|
|
|
Version 9
|
|
* Upgraded/modified documentation a bit.
|
|
* Refactored the code for easier implementation of other output formats.
|
|
* When dumping a client, it now says "client" instead of "datum".
|
|
|
|
Version 8
|
|
* Documented!
|
|
|
|
Version 7
|
|
* Savefile dump now available!
|
|
* Added savefile test and lots of other stuff to the demo.
|
|
* "brief" var added, to display no information about datums, just their name, type and reference.
|
|
It can be enabled in the var_dump constructor.
|
|
* Added a brief datum to html and text output, for auto-enabling the brief var.
|
|
(/var_dump/html/brief for example)
|
|
* Sorry, documentation has to wait until v.8. But now it's about the only thing to add.
|
|
|
|
Version 6
|
|
* Fixed a small bug in _datum() output.
|
|
* The documentation have to wait until v.7. But hopefully that version will have savefile dumping as well.
|
|
|
|
Version 5
|
|
* Added many kinds of dumper datums (Thanks to YMIHere for the suggestion).
|
|
Until documentation is finished (v.6), look at the procs below to see how they work.
|
|
|
|
Version 4
|
|
* Improved the demo a bit more.
|
|
* Moved everything into its own datum, "var_dumper".
|
|
* Reflist is now used for all objects in browser mode, not only for each argument to var_dump().
|
|
* Now all already referenced objects are linked to the original object.
|
|
* Changed the display for associated lists from "=>" to "=", to be more similar to Byonds format.
|
|
* Renamed "shallow" to "depth", to control how many levels to dump.
|
|
|
|
Version 3
|
|
* Added color for procs and verbs.
|
|
* Every datum is now displayed with a reference.
|
|
* browser_dump() and browser_dump_s() can now be used to display output in the browser window!
|
|
The advantage is that the object references are hyperlinked to the original object.
|
|
The first mob logging into the world will be the target for the browser output.
|
|
(Just remember to add . = ..() if you're overriding mob/Login().)
|
|
|
|
Version 2
|
|
* /client can now be dumped.
|
|
* mob.verbs is now working.
|
|
* Improved the demo.
|
|
|
|
*/
|
|
|
|
proc/var_dump()
|
|
if(!args.len) return
|
|
var/var_dump/html/D = new
|
|
var/output = D.ListDump(args)
|
|
boutput(world, output)
|
|
|
|
proc/var_dump_s()
|
|
if(!args.len) return
|
|
var/var_dump/html/D = new(1)
|
|
var/output = D.ListDump(args)
|
|
boutput(world, output)
|
|
|
|
proc/browser_dump()
|
|
if(!args.len) return
|
|
var/var_dump/xhtml/D = new
|
|
var/output = D.ListDump(args)
|
|
world << browse(output)
|
|
|
|
proc/browser_dump_s()
|
|
if(!args.len) return
|
|
var/var_dump/xhtml/D = new(1)
|
|
var/output = D.ListDump(args)
|
|
world << browse(output)
|
|
|
|
////////////////////////////////////////////////////////////
|
|
|
|
var_dump
|
|
|
|
// depth = how many levels to go down recursive.
|
|
// brief = if 1, display only datum name, type and reference, don't scan for vars in it.
|
|
New(depth = 4, brief = 0)
|
|
src.depth = depth
|
|
src.brief = brief
|
|
|
|
src.dump_output = ""
|
|
|
|
var
|
|
depth
|
|
deep_list_length = 50
|
|
|
|
brief
|
|
dump_output
|
|
|
|
list/_reflist
|
|
|
|
proc/ListDump(list/Args)
|
|
_reflist = new
|
|
_dump(Args)
|
|
_reflist = null
|
|
return Output()
|
|
|
|
proc/Dump()
|
|
return ListDump(args)
|
|
|
|
proc/Output()
|
|
return src.dump_output
|
|
|
|
proc/_islist(L)
|
|
return (istype(L, /list))
|
|
|
|
////////////////////////////////
|
|
|
|
proc
|
|
_header()
|
|
_footer()
|
|
|
|
_lineheader()
|
|
_linefooter()
|
|
|
|
proc/_listinfo(list/L)
|
|
boutput(world, "_listinfo needs to be redefined in its own child class.")
|
|
|
|
proc/_text(t, type = null, pos = 0, prefix = "")
|
|
boutput(world, "_text needs to be redefined in its own child class.")
|
|
|
|
proc/_datuminfo(datum/D, type, link = 0)
|
|
boutput(world, "_datuminfo needs to be redefined in its own child class.")
|
|
|
|
proc/_savefileinfo(savefile/S, link = 0)
|
|
boutput(world, "_savefileinfo needs to be redefined in its own child class.")
|
|
|
|
proc/_savefiledir(dir)
|
|
boutput(world, "_savefiledir needs to be redefined in its own child class.")
|
|
|
|
////////////////////////////////
|
|
|
|
// Main output function, creates an output line.
|
|
proc/_oneline(t, type = null, pos = 0, prefix = "")
|
|
var/text = _text(t, type, prefix)
|
|
src.dump_output += _lineheader(pos) + text + _linefooter()
|
|
|
|
|
|
// The main loop, which will process all inputs and append header/footer.
|
|
proc/_dump(list/Args)
|
|
src.dump_output = _header()
|
|
|
|
src.dump_output += _lineheader(0) + _linefooter()
|
|
for(var/a in Args)
|
|
_input(a)
|
|
src.dump_output += _lineheader(0) + _linefooter()
|
|
|
|
src.dump_output += _footer()
|
|
|
|
|
|
// This proc takes care of all inputs and determines their type.
|
|
proc/_input(a, pos = 0, prefix = "", depth = 0)
|
|
|
|
if(istext(a)) _oneline("\"[a]\"", "string", pos, prefix)
|
|
else if(isnum(a)) _oneline(a, "num", pos, prefix)
|
|
else if(isicon(a)) _oneline("'[a]'", "icon", pos, prefix)
|
|
else if(isnull(a)) _oneline("null", "null", pos, prefix)
|
|
else if(isfile(a)) _oneline(a, "file", pos, prefix)
|
|
else if(ispath(a)) _oneline(a, "path", pos, prefix)
|
|
|
|
else if(istype(a, /datum) || istype(a, /client))
|
|
// Test if datum already is referenced.
|
|
if(a in _reflist)
|
|
var/info = _datuminfo(a, _datumtype(a), 1)
|
|
_oneline(info, "self", pos, prefix)
|
|
else if(brief)
|
|
var/info = _datuminfo(a, _datumtype(a))
|
|
_oneline(info, null, pos, prefix)
|
|
else
|
|
_datum(a, pos, prefix, depth)
|
|
|
|
else if(istype(a, /savefile))
|
|
// Test if datum already is referenced.
|
|
if(a in _reflist)
|
|
var/info = _savefileinfo(a, 1)
|
|
_oneline(info, "self", pos, prefix)
|
|
else if(brief)
|
|
var/info = _savefileinfo(a)
|
|
_oneline(info, null, pos, prefix)
|
|
else
|
|
_savefile(a, pos, prefix, depth)
|
|
|
|
else if(_islist(a)) _list(a, pos, prefix, depth)
|
|
else _oneline("[a]()", "proc", pos, prefix) // Assume that default type is proc for now.
|
|
|
|
|
|
proc/_savefile_dump(savefile/S, cur_dir, pos, depth)
|
|
|
|
var
|
|
a // Temp var for savefile content
|
|
next_pos = pos
|
|
|
|
S.cd = cur_dir
|
|
S.eof = 0
|
|
|
|
_oneline(_savefiledir(S.cd), null, next_pos)
|
|
|
|
while(!S.eof)
|
|
S >> a
|
|
_input(a, next_pos+2, "", depth+1)
|
|
|
|
for(var/d in S.dir)
|
|
_savefile_dump(S, d, next_pos+2, depth+1)
|
|
S.cd = ".."
|
|
|
|
proc/_savefile(savefile/S, pos = 0, prefix = "", depth = 0)
|
|
|
|
var/next_pos = pos
|
|
|
|
if(prefix)
|
|
_oneline("[prefix]", null, next_pos)
|
|
next_pos += 2
|
|
|
|
_oneline(_savefileinfo(S), null, next_pos)
|
|
next_pos += 2
|
|
|
|
_reflist += S
|
|
|
|
_savefile_dump(S, "/", next_pos, depth+1)
|
|
|
|
|
|
proc/_datum(datum/D, pos = 0, prefix = "", depth = 0)
|
|
|
|
var
|
|
info
|
|
type = _datumtype(D)
|
|
next_pos = pos
|
|
|
|
if(prefix)
|
|
_oneline("[prefix]", null, next_pos)
|
|
next_pos += 2
|
|
|
|
_oneline(_datuminfo(D, type), null, next_pos)
|
|
next_pos += 2
|
|
|
|
// Add datum to reference list
|
|
_reflist += D
|
|
|
|
//output = _text(info, null, pos, prefix)
|
|
|
|
// Now start checking out the datum vars.
|
|
var/list/varlist = D.vars.Copy()
|
|
|
|
// Remove the vars list from /atom, since it's pretty much selfreferencing the atom.
|
|
varlist.Remove("vars")
|
|
|
|
prefix = ""
|
|
|
|
for(var/V in varlist)
|
|
prefix = "'[V]' = "
|
|
|
|
if(D.vars[V] == D)
|
|
_oneline("self", "self", next_pos, prefix)
|
|
else if(src.depth && depth+1 >= src.depth && (istype(D.vars[V], /datum) || istype(D.vars[V], /client)))
|
|
|
|
if(D.vars[V] in _reflist)
|
|
info = _datuminfo(D.vars[V], type, 1)
|
|
else
|
|
info = _datuminfo(D.vars[V], type)
|
|
|
|
_oneline(info, null, next_pos, prefix)
|
|
else
|
|
_input(D.vars[V], next_pos, prefix, depth+1)
|
|
|
|
|
|
proc/_list(list/L, pos = 0, prefix = "", depth = 0)
|
|
|
|
// Text, Typ, Position, Prefix
|
|
|
|
var/next_pos = pos
|
|
var
|
|
deep = 0
|
|
old_brief
|
|
|
|
if(prefix)
|
|
_oneline("[prefix]", null, next_pos)
|
|
next_pos += 2
|
|
|
|
_oneline(_listinfo(L), null, next_pos)
|
|
next_pos += 2
|
|
|
|
if(L.len > src.deep_list_length)
|
|
old_brief = src.brief
|
|
src.brief = 1
|
|
deep = 1
|
|
|
|
var/count = 1
|
|
for(var/content in L)
|
|
// Test for self-referencing
|
|
if(content == L) _oneline("self", "self", next_pos, "'[count]' = ")
|
|
else if(istext(content) && L[content] == L) _oneline("self", "self", next_pos, "'[content]' = ")
|
|
|
|
else if(!istext(content))
|
|
_input(content, next_pos, "[count] = ", depth+1)
|
|
|
|
else
|
|
_input(L[content], next_pos, "'[content]' = ", depth+1)
|
|
|
|
count++
|
|
|
|
if(deep)
|
|
src.brief = old_brief
|
|
|
|
proc/_datumtype(datum/D)
|
|
|
|
// Extract the base type from the type path.
|
|
var/slashpos = findtext("[D.type]", "/", 2)
|
|
var/type = copytext("[D.type]", 2, slashpos)
|
|
|
|
switch(type)
|
|
if("area","turf","mob","obj","client","list") return type
|
|
else return "datum"
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
text
|
|
_text(t, type = null, prefix = "")
|
|
return "[prefix][t]"
|
|
|
|
_savefileinfo(savefile/S, link = 0)
|
|
return "savefile([S.name]) \ref[S]"
|
|
|
|
_savefiledir(dir)
|
|
return dir
|
|
|
|
_datuminfo(datum/D, type, link = 0)
|
|
return "[type]([D.type]) \ref[D]"
|
|
|
|
_lineheader(pos)
|
|
var/spaces = ""
|
|
while(pos--)
|
|
spaces += " "
|
|
|
|
return spaces
|
|
|
|
_linefooter()
|
|
return "<br>"
|
|
|
|
_listinfo(list/L)
|
|
return "list"
|
|
|
|
//////////////////////////////////////////////////
|
|
|
|
html
|
|
var/list/_colors = list("string" = "#BB00BB", "num" = "#00BB00", "null" = "#FF0000", "proc" = "#0000BB", \
|
|
"icon" = "#0000FF", "path" = "#BBBB00", "self" = "#00BBBB", "file" = "#FF8C18")
|
|
|
|
_savefileinfo(savefile/S, link = 0)
|
|
var/output = "<strong>savefile</strong>([S.name])"
|
|
var/valid_link = copytext("\ref[S]", 2, length("\ref[S]"))
|
|
if(link)
|
|
// Make link
|
|
return output + " <a href='#[valid_link]'>\ref[S]</a>"
|
|
else
|
|
// Anchor
|
|
return output + " <a name='[valid_link]'>\ref[S]</a>"
|
|
|
|
_savefiledir(dir)
|
|
return "<font color=#FF0000><strong>[dir]</strong></font>"
|
|
|
|
_datuminfo(datum/D, type, link = 0)
|
|
|
|
var/valid_link = copytext("\ref[D]", 2, length("\ref[D]"))
|
|
|
|
if(link)
|
|
// Make link
|
|
return "<strong>[type]</strong>(<i>[D.type]</i>) <a href='#[valid_link]'>\ref[D]</a>"
|
|
else
|
|
// Anchor
|
|
return "<strong>[type]</strong>(<i>[D.type]</i>) <a name='[valid_link]'>\ref[D]</a>"
|
|
|
|
_text(t, type = null, prefix = "")
|
|
|
|
var/color = "#000000"
|
|
if(type) color = _colors[type]
|
|
|
|
return "[prefix]<font color='[color]'>[t]</font>"
|
|
|
|
_header()
|
|
return "<br>"
|
|
|
|
_footer()
|
|
return "<br>"
|
|
|
|
_lineheader(pos)
|
|
|
|
var/spaces = ""
|
|
while(pos--)
|
|
spaces += " "
|
|
|
|
return "<tt>[spaces]"
|
|
|
|
_linefooter()
|
|
return "</tt><br>"
|
|
|
|
_listinfo(list/L)
|
|
return "<strong>list</strong>"
|
|
|
|
|
|
xhtml
|
|
var/list/_colors = list("string" = "#BB00BB", "num" = "#00BB00", "null" = "#FF0000", "proc" = "#0000BB", \
|
|
"icon" = "#0000FF", "path" = "#BBBB00", "self" = "#00BBBB", "file" = "#FF8C18")
|
|
|
|
_savefileinfo(savefile/S, link = 0)
|
|
var/output = "<strong>savefile</strong>([S.name])"
|
|
var/valid_link = copytext("\ref[S]", 2, length("\ref[S]"))
|
|
if(link)
|
|
// Make link
|
|
return output + " <a href='#[valid_link]'>\ref[S]</a>" + ascii2text(13) + ascii2text(10)
|
|
else
|
|
// Anchor
|
|
return output + " <a name='[valid_link]'>\ref[S]</a>" + ascii2text(13) + ascii2text(10)
|
|
|
|
_savefiledir(dir)
|
|
return "<span style='color:#FF0000;'><strong>[dir]</strong></span>"
|
|
|
|
_datuminfo(datum/D, type, link = 0)
|
|
|
|
var/valid_link = copytext("\ref[D]", 2, length("\ref[D]"))
|
|
|
|
if(link)
|
|
// Make link
|
|
return "<strong>[type]</strong>(<i>[D.type]</i>) <a href='#[valid_link]'>\ref[D]</a>" + ascii2text(13) + ascii2text(10)
|
|
else
|
|
// Anchor
|
|
return "<strong>[type]</strong>(<i>[D.type]</i>) <a name='[valid_link]'>\ref[D]</a>" + ascii2text(13) + ascii2text(10)
|
|
|
|
_text(t, type = null, prefix = "")
|
|
|
|
var/color = ""
|
|
if(type) color = " style='color:[_colors[type]];'"
|
|
|
|
return "[prefix]<span[color]>[t]</span>"
|
|
|
|
_header()
|
|
var/linebreak = ascii2text(13) + ascii2text(10)
|
|
return "\
|
|
<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'>[linebreak]\
|
|
<html xmlns='http://www.w3.org/1999/xhtml' lang='en' xml:lang='en'>[linebreak]\
|
|
<head><title>var_dump output</title></head>[linebreak]\
|
|
<body><div style='font-family: courier new, courier, monotype; font-size:10pt;'>[linebreak]"
|
|
|
|
_footer()
|
|
var/linebreak = ascii2text(13) + ascii2text(10)
|
|
return "[linebreak]</div></body></html>"
|
|
|
|
_lineheader(pos)
|
|
var/spaces = ""
|
|
while(pos--)
|
|
spaces += " "
|
|
|
|
return spaces
|
|
|
|
_linefooter()
|
|
return "<br>" + ascii2text(13) + ascii2text(10)
|
|
|
|
_listinfo(list/L)
|
|
return "<strong>list</strong>"
|