Skip to content

Commit

Permalink
513 Stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
APWill authored and APWill committed Mar 5, 2020
1 parent 0c1cd02 commit e67975b
Show file tree
Hide file tree
Showing 110 changed files with 1,092 additions and 3,999 deletions.
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ notifications:

env:
global:
- BYOND_MAJOR="512"
- BYOND_MINOR="1488"
- BYOND_MAJOR="513"
- BYOND_MINOR="1511"
- DM_BUILDFILE="odessa-outpost.dme"

cache:
Expand Down
Binary file added byond-extools.dll
Binary file not shown.
153 changes: 153 additions & 0 deletions code/__DEFINES/_extools_api.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
#define EXTOOLS (world.system_type == MS_WINDOWS ? "byond-extools.dll" : "./libbyond-extools.so")
#define EXTOOLS_SUCCESS "SUCCESS"
#define EXTOOLS_FAILED "FAIL"
#define GLOBAL_PROC "magic BS"

/*
Core - Provides necessary functionality for other modules.
Initializing any other modules also initializes this so it shouldn't be necessary to call this.
*/

/proc/extools_initialize()
call(EXTOOLS, "cleanup")()
return call(EXTOOLS, "core_initialize")() == EXTOOLS_SUCCESS

/*
TFFI - Threaded FFI
All DLL calls are automatically threaded off.
Black magic is used to suspend (sleep) the currently executing proc, allowing non-blocking FFI.
You may call a DLL function and sleep until it returns, pass a callback to be called with the result,
or call resolve() on the /datum/promise to receive the return value at any time.
Example:
var/x = call_wait("sample.dll", "do_work", "arg1", "arg2", "arg3")
- Calls the do_work function from sample.dll with 3 arguments. The proc sleeps until do_work returns.
var/datum/promise/P = call_async("sample.dll", "do_work", "arg1")
... do something else ...
var/result = P.resolve()
- Calls do_work with 1 argument. Returns a promise object. Runs some other code before calling P.resolve() to obtain the result.
/proc/print_result(result)
world << result
call_cb("sample.dll", "do_work", /proc/print_result, "arg1", "arg2")
- Calls do_work with 2 arguments. The callback is invoked with the result as the single argument. Execution resumes immediately.
*/

/proc/tffi_initialize()
return call(EXTOOLS, "tffi_initialize")() == EXTOOLS_SUCCESS

/datum/promise
var/completed = FALSE
var/result = ""
var/callback_context = GLOBAL_PROC
var/callback_proc = null
var/__id = 0

/datum/promise/New()
__id = GLOB.next_promise_id++ //please don't create more than 10^38 promises in a single tick

//This proc's bytecode is overwritten to allow suspending and resuming on demand.
//None of the code here should run.
/datum/promise/proc/__internal_resolve(ref, id)
if(!GLOB.fallback_alerted && world.system_type != UNIX) // the rewriting is currently broken on Linux.
world << "<b>TFFI: __internal_resolve has not been rewritten, the TFFI DLL was not loaded correctly.</b>"
world.log << "<b>TFFI: __internal_resolve has not been rewritten, the TFFI DLL was not loaded correctly.</b>"
GLOB.fallback_alerted = TRUE
while(!completed)
sleep(1)
//It might be better to just fail and notify the user that something went wrong.

/datum/promise/proc/__resolve_callback()
__internal_resolve("\ref[src]", __id)
if(callback_context == GLOBAL_PROC)
call(callback_proc)(result)
else
call(callback_context, callback_proc)(result)

/datum/promise/proc/resolve()
__internal_resolve("\ref[src]", __id)
return result

/proc/call_async()
var/list/arguments = args.Copy()
var/datum/promise/P = new
arguments.Insert(1, "\ref[P]")
call(EXTOOLS, "call_async")(arglist(arguments))
return P

/proc/call_cb()
var/list/arguments = args.Copy()
var/context = arguments[3]
var/callback = arguments[4]
arguments.Cut(3, 5)
var/datum/promise/P = new
P.callback_context = context
P.callback_proc = callback
arguments.Insert(1, "\ref[P]")
call(EXTOOLS, "call_async")(arglist(arguments))
spawn(0)
P.__resolve_callback()

/proc/call_wait()
return call_async(arglist(args)).resolve()

/*
Extended Profiling - High precision in-depth performance profiling.
Turning on extended profiling for a proc will cause each execution of it to generate a file in the ./profiles directory
containing a breakdown of time spent executing the proc and each sub-proc it calls. Import the file into https://www.speedscope.app/ to
view a good visual representation.
Be aware that sleeping counts as stopping and restarting the execution of the proc, which will generate multiple files, one between each sleep.
For large procs the profiles may become unusably large. Optimizations pending.
Example:
start_profiling(/datum/explosion/New)
- Enables profiling for /datum/explosion/New(), which will produce a detailed breakdown of each explosion that occurs afterwards.
stop_profiling(/datum/explosion/New)
- Disables profiling for explosions. Any currently running profiles will stop when the proc finishes executing or enters a sleep.
*/

/proc/profiling_initialize()
return call(EXTOOLS, "extended_profiling_initialize")() == EXTOOLS_SUCCESS

/proc/start_profiling(procpath)
call(EXTOOLS, "enable_extended_profiling")("[procpath]")

/proc/stop_profiling(procpath)
call(EXTOOLS, "disable_extended_profiling")("[procpath]")

/*
Debug Server - High and low level debugging of DM code.
Calling debugger_initialize will start a debug server that allows connections from frontends,
such as SpaceManiac's VSCode extension for line-by-line debugging (and more), or Steamport's
Somnium for bytecode inspection.
Call with pause = TRUE to wait until the debugger connected and immediately break on the next instruction after the call.
*/

/proc/debugger_initialize(pause = FALSE)
if(world.system_type == MS_WINDOWS)
return call(EXTOOLS, "debug_initialize")(pause ? "pause" : "") == EXTOOLS_SUCCESS

/*
Misc
*/

//Programatically enable and disable the built-in byond profiler. Useful if you want to, for example, profile subsystem initializations.
/proc/enable_profiling()
return call(EXTOOLS, "enable_profiling")() == EXTOOLS_SUCCESS

/proc/disable_profiling()
return call(EXTOOLS, "disable_profiling")() == EXTOOLS_SUCCESS

// MAPTICK STUFF //
/proc/maptick_initialize()
return call(EXTOOLS, "maptick_initialize")() == EXTOOLS_SUCCESS
2 changes: 1 addition & 1 deletion code/__DEFINES/callbacks.dm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#define GLOBAL_PROC "some_magic_bullshit"
//#define GLOBAL_PROC "some_magic_bullshit"

#define CALLBACK new /datum/callback
#define INVOKE_ASYNC ImmediateInvokeAsync
Expand Down
7 changes: 6 additions & 1 deletion code/__DEFINES/maths.dm
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,9 @@
sum += val
return sum / args.len

// )
// )

// Round up
proc/n_ceil(var/num)
if(isnum(num))
return round(num)+1
1 change: 1 addition & 0 deletions code/__DEFINES/subsystems-priority.dm
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var/list/bitflags = list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096
#define SS_PRIORITY_OVERLAYS 500
#define SS_PRIORITY_TICKER 200 // Gameticker processing.
#define SS_PRIORITY_MOB 100 // Mob Life().
#define SS_PRIORITY_CHAT 100 // Chat subsystem.
#define SS_PRIORITY_MACHINERY 100 // Machinery + powernet ticks.
#define SS_PRIORITY_AIR 80 // ZAS processing.
#define SS_PRIORITY_ALARM 20 // Alarm processing.
Expand Down
1 change: 1 addition & 0 deletions code/__DEFINES/subsystems.dm
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
#define INIT_OPEN_SPACE -150
#define INIT_ORDER_CRAFT -175
#define INIT_ORDER_LATELOAD -180
#define INIT_ORDER_CHAT -185

// SS runlevels

Expand Down
7 changes: 6 additions & 1 deletion code/__DEFINES/tick.dm
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
#define TICK_LIMIT_RUNNING 80
// Maptick stuff
#define MAPTICK_FALLBACK_ITU 20 //If no maptick DLL is present, it's assumed to be this value during server runtime
#define MAPTICK_MC_MIN_RESERVE 20 //Percentage of tick to leave for master controller to run
#define MAPTICK_LAST_INTERNAL_TICK_USAGE ((GLOB.internal_tick_usage / world.tick_lag) * 100) //internal_tick_usage is updated every tick by extools
#define TICK_LIMIT_RUNNING (max(90 - MAPTICK_LAST_INTERNAL_TICK_USAGE, MAPTICK_MC_MIN_RESERVE))

#define TICK_LIMIT_TO_RUN 78
#define TICK_LIMIT_MC 70
#define TICK_LIMIT_MC_INIT_DEFAULT 98
Expand Down
2 changes: 1 addition & 1 deletion code/__HELPERS/files.dm
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
continue
path += choice

if(copytext(path, -1, 0) != "/") //didn't choose a directory, no need to iterate again
if(copytext_char(path, -1) != "/") //didn't choose a directory, no need to iterate again
break

var/extension = copytext(path, -4, 0)
Expand Down
1 change: 1 addition & 0 deletions code/__HELPERS/logging.dm
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//wrapper macros for easier grepping
#define DIRECT_OUTPUT(A, B) A << B
#define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text)
#define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text)
//print an error message to world.log

Expand Down
40 changes: 27 additions & 13 deletions code/__HELPERS/sanitize_values.dm
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,34 @@
else return default
return default

/proc/sanitize_hexcolor(color, default="#000000")
if(!istext(color)) return default
/proc/sanitize_hexcolor(color, default="#000000", desired_format=6, include_crunch=TRUE)
var/crunch = include_crunch ? "#" : ""
if(!istext(color))
color = ""

var/start = 1 + (text2ascii(color, 1) == 35)
var/len = length(color)
if(len != 7 && len !=4) return default
if(text2ascii(color, 1) != 35) return default //35 is the ascii code for "#"
. = "#"
for(var/i=2, i<=len, i++)
var/ascii = text2ascii(color, i)
switch(ascii)
if(48 to 57) . += ascii2text(ascii) //numbers 0 to 9
if(97 to 102) . += ascii2text(ascii) //letters a to f
if(65 to 70) . += ascii2text(ascii+32) //letters A to F - translates to lowercase
else return default
return .
var/char = ""

. = ""
for(var/i = start, i <= len, i += length(char))
char = color[i]
switch(text2ascii(char))
if(48 to 57) //numbers 0 to 9
. += char
if(97 to 102) //letters a to f
. += char
if(65 to 70) //letters A to F
. += lowertext(char)
else
break

if(length_char(.) != desired_format)
if(default)
return default
return crunch + repeat_string(desired_format, "0")

return crunch + .

//Valid format codes: YY, YEAR, MM, DD, hh, mm, ss, :, -. " " (space). Invalid format will return default.
/proc/sanitize_time(time, default, format = "hh:mm")
Expand Down
88 changes: 61 additions & 27 deletions code/__HELPERS/text.dm
Original file line number Diff line number Diff line change
Expand Up @@ -127,17 +127,30 @@
return output

//Returns null if there is any bad text in the string
/proc/reject_bad_text(var/text, var/max_length=512)
if(length(text) > max_length) return //message too long
var/non_whitespace = 0
for(var/i=1, i<=length(text), i++)
switch(text2ascii(text, i))
if(62, 60, 92, 47) return //rejects the text if it contains these bad characters: <, >, \ or /
if(127 to 255) return //rejects weird letters like �
if(0 to 31) return //more weird stuff
if(32) continue //whitespace
else non_whitespace = 1
if(non_whitespace) return text //only accepts the text if it has some non-spaces
/proc/reject_bad_text(text, max_length = 512, ascii_only = TRUE)
var/char_count = 0
var/non_whitespace = FALSE
var/lenbytes = length(text)
var/char = ""
for(var/i = 1, i <= lenbytes, i += length(char))
char = text[i]
char_count++
if(char_count > max_length)
return
switch(text2ascii(char))
if(62, 60, 92, 47) // <, >, \, /
return
if(0 to 31)
return
if(32)
continue
if(127 to INFINITY)
if(ascii_only)
return
else
non_whitespace = TRUE
if(non_whitespace)
return text //only accepts the text if it has some non-spaces


//Old variant. Haven't dared to replace in some places.
Expand Down Expand Up @@ -272,40 +285,55 @@
//This proc fills in all spaces with the "replace" var (* by default) with whatever
//is in the other string at the same spot (assuming it is not a replace char).
//This is used for fingerprints
/proc/stringmerge(var/text, var/compare, replace = "*")
/proc/stringmerge(text,compare,replace = "*")
//This proc fills in all spaces with the "replace" var (* by default) with whatever
//is in the other string at the same spot (assuming it is not a replace char).
//This is used for fingerprints
var/newtext = text
if(length(text) != length(compare))
return 0
for(var/i = 1, i < length(text), i++)
var/a = copytext(text, i, i+1)
var/b = copytext(compare, i, i+1)
//if it isn't both the same letter, or if they are both the replacement character
//(no way to know what it was supposed to be)
var/text_it = 1 //iterators
var/comp_it = 1
var/newtext_it = 1
var/text_length = length(text)
var/comp_length = length(compare)
while(comp_it <= comp_length && text_it <= text_length)
var/a = text[text_it]
var/b = compare[comp_it]
//if it isn't both the same letter, or if they are both the replacement character
//(no way to know what it was supposed to be)
if(a != b)
if(a == replace) //if A is the replacement char
newtext = copytext(newtext, 1, i) + b + copytext(newtext, i+1)
newtext = copytext(newtext, 1, newtext_it) + b + copytext(newtext, newtext_it + length(newtext[newtext_it]))
else if(b == replace) //if B is the replacement char
newtext = copytext(newtext, 1, i) + a + copytext(newtext, i+1)
newtext = copytext(newtext, 1, newtext_it) + a + copytext(newtext, newtext_it + length(newtext[newtext_it]))
else //The lists disagree, Uh-oh!
return 0
text_it += length(a)
comp_it += length(b)
newtext_it += length(newtext[newtext_it])

return newtext

//This proc returns the number of chars of the string that is the character
//This is used for detective work to determine fingerprint completion.
/proc/stringpercent(var/text, character = "*")
/proc/stringpercent(text,character = "*")
if(!text || !character)
return 0
var/count = 0
for(var/i = 1, i <= length(text), i++)
var/a = copytext(text, i, i+1)
var/lentext = length(text)
var/a = ""
for(var/i = 1, i <= lentext, i += length(a))
a = text[i]
if(a == character)
count++
return count

/proc/reverse_text(var/text = "")
/proc/reverse_text(text = "")
var/new_text = ""
for(var/i = length(text); i > 0; i--)
new_text += copytext(text, i, i+1)
var/lentext = length(text)
var/letter = ""
for(var/i = 1, i <= lentext, i += length(letter))
letter = text[i]
new_text = letter + new_text
return new_text

//Used in preferences' SetFlavorText and human's set_flavor verb
Expand Down Expand Up @@ -553,3 +581,9 @@ proc/TextPreview(var/string, var/len=40)
. = base
if(rest)
. += .(rest)


/proc/repeat_string(times, string="")
. = ""
for(var/i=1, i<=times, i++)
. += string
Loading

0 comments on commit e67975b

Please sign in to comment.