mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 10:21:11 +00:00
Update NTSL from /tg/.
This commit is contained in:
@@ -69,11 +69,15 @@
|
||||
//
|
||||
IfStatement
|
||||
var
|
||||
skip = 0
|
||||
node
|
||||
BlockDefinition
|
||||
block
|
||||
else_block //may be null
|
||||
expression/cond
|
||||
statement/else_if
|
||||
|
||||
ElseIf
|
||||
|
||||
/*
|
||||
Class: WhileLoop
|
||||
|
||||
@@ -60,6 +60,15 @@
|
||||
New(name, token/t)
|
||||
message="Function '[name]' defined twice."
|
||||
|
||||
ParameterFunction
|
||||
message = "You cannot use a function inside a parameter."
|
||||
|
||||
New(token/t)
|
||||
var/line = "?"
|
||||
if(t)
|
||||
line = t.line
|
||||
message = "[line]: [message]"
|
||||
|
||||
/*
|
||||
Class: runtimeError
|
||||
An error thrown by the interpreter in running the script.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
client/verb/tcssave()
|
||||
set hidden = 1
|
||||
if(mob.machine || issilicon(mob))
|
||||
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || issilicon(mob))
|
||||
if(telecomms_check(mob))
|
||||
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
|
||||
if(Machine.editingcode != mob)
|
||||
return
|
||||
@@ -9,9 +9,6 @@ client/verb/tcssave()
|
||||
if(Machine.SelectedServer)
|
||||
var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
|
||||
var/tcscode=winget(src, "tcscode", "text")
|
||||
var/msg="[mob.name] is adding script to server [Server]: [tcscode]"
|
||||
diary << msg
|
||||
message_admins("[mob.name] has uploaded a NTLS script to [Machine.SelectedServer] ([mob.x],[mob.y],[mob.z] - <A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[mob.x];Y=[mob.y];Z=[mob.z]'>JMP</a>)",0,1)
|
||||
Server.setcode( tcscode ) // this actually saves the code from input to the server
|
||||
src << output(null, "tcserror") // clear the errors
|
||||
else
|
||||
@@ -28,7 +25,7 @@ client/verb/tcssave()
|
||||
client/verb/tcscompile()
|
||||
set hidden = 1
|
||||
if(mob.machine || issilicon(mob))
|
||||
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
|
||||
if(telecomms_check(mob))
|
||||
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
|
||||
if(Machine.editingcode != mob)
|
||||
return
|
||||
@@ -36,35 +33,40 @@ client/verb/tcscompile()
|
||||
if(Machine.SelectedServer)
|
||||
var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
|
||||
Server.setcode( winget(src, "tcscode", "text") ) // save code first
|
||||
var/list/compileerrors = Server.compile() // then compile the code!
|
||||
|
||||
// Output all the compile-time errors
|
||||
src << output(null, "tcserror")
|
||||
spawn(0)
|
||||
// Output all the compile-time errors
|
||||
src << output(null, "tcserror")
|
||||
src << output("<font color = black>Please wait, compiling...</font>", "tcserror")
|
||||
|
||||
if(compileerrors.len)
|
||||
src << output("<b>Compile Errors</b>", "tcserror")
|
||||
for(var/scriptError/e in compileerrors)
|
||||
src << output("<font color = red>\t>[e.message]</font color>", "tcserror")
|
||||
src << output("([compileerrors.len] errors)", "tcserror")
|
||||
var/list/compileerrors = Server.compile(mob) // then compile the code!
|
||||
if(!telecomms_check(mob))
|
||||
return
|
||||
|
||||
// Output compile errors to all other people viewing the code too
|
||||
for(var/mob/M in Machine.viewingcode)
|
||||
if(M.client)
|
||||
M << output(null, "tcserror")
|
||||
M << output("<b>Compile Errors</b>", "tcserror")
|
||||
for(var/scriptError/e in compileerrors)
|
||||
M << output("<font color = red>\t>[e.message]</font color>", "tcserror")
|
||||
M << output("([compileerrors.len] errors)", "tcserror")
|
||||
if(compileerrors.len)
|
||||
src << output("<b>Compile Errors</b>", "tcserror")
|
||||
for(var/scriptError/e in compileerrors)
|
||||
src << output("<font color = red>\t>[e.message]</font color>", "tcserror")
|
||||
src << output("([compileerrors.len] errors)", "tcserror")
|
||||
|
||||
// Output compile errors to all other people viewing the code too
|
||||
for(var/mob/M in Machine.viewingcode)
|
||||
if(M.client)
|
||||
M << output(null, "tcserror")
|
||||
M << output("<b>Compile Errors</b>", "tcserror")
|
||||
for(var/scriptError/e in compileerrors)
|
||||
M << output("<font color = red>\t>[e.message]</font color>", "tcserror")
|
||||
M << output("([compileerrors.len] errors)", "tcserror")
|
||||
|
||||
|
||||
else
|
||||
src << output("<font color = blue>TCS compilation successful!</font color>", "tcserror")
|
||||
src << output("(0 errors)", "tcserror")
|
||||
else
|
||||
src << output("<font color = blue>TCS compilation successful!</font color>", "tcserror")
|
||||
src << output("(0 errors)", "tcserror")
|
||||
|
||||
for(var/mob/M in Machine.viewingcode)
|
||||
if(M.client)
|
||||
M << output("<font color = blue>TCS compilation successful!</font color>", "tcserror")
|
||||
M << output("(0 errors)", "tcserror")
|
||||
for(var/mob/M in Machine.viewingcode)
|
||||
if(M.client)
|
||||
M << output("<font color = blue>TCS compilation successful!</font color>", "tcserror")
|
||||
M << output("(0 errors)", "tcserror")
|
||||
|
||||
else
|
||||
src << output(null, "tcserror")
|
||||
@@ -79,56 +81,26 @@ client/verb/tcscompile()
|
||||
client/verb/tcsrun()
|
||||
set hidden = 1
|
||||
if(mob.machine || issilicon(mob))
|
||||
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
|
||||
if(telecomms_check(mob))
|
||||
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
|
||||
if(Machine.editingcode != mob)
|
||||
return
|
||||
|
||||
if(Machine.SelectedServer)
|
||||
var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
|
||||
Server.setcode( winget(src, "tcscode", "text") ) // save code first
|
||||
var/list/compileerrors = Server.compile() // then compile the code!
|
||||
|
||||
// Output all the compile-time errors
|
||||
src << output(null, "tcserror")
|
||||
|
||||
if(compileerrors.len)
|
||||
src << output("<b>Compile Errors</b>", "tcserror")
|
||||
for(var/scriptError/e in compileerrors)
|
||||
src << output("<font color = red>\t>[e.message]</font color>", "tcserror")
|
||||
src << output("([compileerrors.len] errors)", "tcserror")
|
||||
|
||||
// Output compile errors to all other people viewing the code too
|
||||
for(var/mob/M in Machine.viewingcode)
|
||||
if(M.client)
|
||||
M << output(null, "tcserror")
|
||||
M << output("<b>Compile Errors</b>", "tcserror")
|
||||
for(var/scriptError/e in compileerrors)
|
||||
M << output("<font color = red>\t>[e.message]</font color>", "tcserror")
|
||||
M << output("([compileerrors.len] errors)", "tcserror")
|
||||
|
||||
var/datum/signal/signal = new()
|
||||
signal.data["message"] = ""
|
||||
if(Server.freq_listening.len > 0)
|
||||
signal.frequency = Server.freq_listening[1]
|
||||
else
|
||||
// Finally, we run the code!
|
||||
src << output("<font color = blue>TCS compilation successful! Code executed.</font color>", "tcserror")
|
||||
src << output("(0 errors)", "tcserror")
|
||||
signal.frequency = 1459
|
||||
signal.data["name"] = ""
|
||||
signal.data["job"] = ""
|
||||
signal.data["reject"] = 0
|
||||
signal.data["server"] = Server
|
||||
|
||||
for(var/mob/M in Machine.viewingcode)
|
||||
if(M.client)
|
||||
M << output("<font color = blue>TCS compilation successful!</font color>", "tcserror")
|
||||
M << output("(0 errors)", "tcserror")
|
||||
|
||||
var/datum/signal/signal = new()
|
||||
signal.data["message"] = ""
|
||||
if(Server.freq_listening.len > 0)
|
||||
signal.frequency = Server.freq_listening[1]
|
||||
else
|
||||
signal.frequency = 1459
|
||||
signal.data["name"] = ""
|
||||
signal.data["job"] = ""
|
||||
signal.data["reject"] = 0
|
||||
signal.data["server"] = Server
|
||||
|
||||
Server.Compiler.Run(signal)
|
||||
Server.Compiler.Run(signal)
|
||||
|
||||
|
||||
else
|
||||
@@ -145,7 +117,7 @@ client/verb/tcsrun()
|
||||
client/verb/exittcs()
|
||||
set hidden = 1
|
||||
if(mob.machine || issilicon(mob))
|
||||
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
|
||||
if(telecomms_check(mob))
|
||||
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
|
||||
if(Machine.editingcode == mob)
|
||||
Machine.storedcode = "[winget(mob, "tcscode", "text")]"
|
||||
@@ -157,7 +129,7 @@ client/verb/exittcs()
|
||||
client/verb/tcsrevert()
|
||||
set hidden = 1
|
||||
if(mob.machine || issilicon(mob))
|
||||
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
|
||||
if(telecomms_check(mob))
|
||||
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
|
||||
if(Machine.editingcode != mob)
|
||||
return
|
||||
@@ -186,7 +158,7 @@ client/verb/tcsrevert()
|
||||
client/verb/tcsclearmem()
|
||||
set hidden = 1
|
||||
if(mob.machine || issilicon(mob))
|
||||
if((istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && mob.machine in view(1, mob)) || (issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) ))
|
||||
if(telecomms_check(mob))
|
||||
var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
|
||||
if(Machine.editingcode != mob)
|
||||
return
|
||||
@@ -208,4 +180,9 @@ client/verb/tcsclearmem()
|
||||
src << output("<font color = red>Failed to clear memory: Unable to locate machine.</font color>", "tcserror")
|
||||
else
|
||||
src << output(null, "tcserror")
|
||||
src << output("<font color = red>Failed to clear memory: Unable to locate machine.</font color>", "tcserror")
|
||||
src << output("<font color = red>Failed to clear memory: Unable to locate machine.</font color>", "tcserror")
|
||||
|
||||
/proc/telecomms_check(var/mob/mob)
|
||||
if(mob && istype(mob.machine, /obj/machinery/computer/telecomms/traffic) && in_range(mob.machine, mob) || issilicon(mob) && istype(mob.machine, /obj/machinery/computer/telecomms/traffic))
|
||||
return 1
|
||||
return 0
|
||||
@@ -10,11 +10,26 @@
|
||||
HandleError(runtimeError/e)
|
||||
Compiler.Holder.add_entry(e.ToString(), "Execution Error")
|
||||
|
||||
GC()
|
||||
..()
|
||||
Compiler = null
|
||||
|
||||
|
||||
/datum/TCS_Compiler
|
||||
|
||||
var/n_Interpreter/TCS_Interpreter/interpreter
|
||||
var/obj/machinery/telecomms/server/Holder // the server that is running the code
|
||||
var/ready = 1 // 1 if ready to run code
|
||||
|
||||
/* -- Set ourselves to Garbage Collect -- */
|
||||
|
||||
proc/GC()
|
||||
|
||||
Holder = null
|
||||
if(interpreter)
|
||||
interpreter.GC()
|
||||
|
||||
|
||||
/* -- Compile a raw block of text -- */
|
||||
|
||||
proc/Compile(code as message)
|
||||
@@ -54,7 +69,9 @@
|
||||
interpreter.SetVar("E" , 2.718281828) // value of e
|
||||
interpreter.SetVar("SQURT2" , 1.414213562) // value of the square root of 2
|
||||
interpreter.SetVar("FALSE" , 0) // boolean shortcut to 0
|
||||
interpreter.SetVar("false" , 0) // boolean shortcut to 0
|
||||
interpreter.SetVar("TRUE" , 1) // boolean shortcut to 1
|
||||
interpreter.SetVar("true" , 1) // boolean shortcut to 1
|
||||
|
||||
interpreter.SetVar("NORTH" , NORTH) // NORTH (1)
|
||||
interpreter.SetVar("SOUTH" , SOUTH) // SOUTH (2)
|
||||
@@ -93,13 +110,13 @@
|
||||
interpreter.SetProc("broadcast", "tcombroadcast", signal, list("message", "freq", "source", "job"))
|
||||
|
||||
/*
|
||||
-> Send a signal used by signallers.
|
||||
-> Send a code signal.
|
||||
@format: signal(frequency, code)
|
||||
|
||||
@param frequency: Frequency to broadcast to
|
||||
@param code: Code to send
|
||||
@param frequency: Frequency to send the signal to
|
||||
@param code: Encryption code to send the signal with
|
||||
*/
|
||||
interpreter.SetProc("signal", /proc/ntsl_send_signal)
|
||||
interpreter.SetProc("signal", "signaler", signal, list("freq", "code"))
|
||||
|
||||
/*
|
||||
-> Store a value permanently to the server machine (not the actual game hosting machine, the ingame machine)
|
||||
@@ -184,8 +201,12 @@
|
||||
interpreter.SetProc("round", /proc/n_round)
|
||||
interpreter.SetProc("clamp", /proc/n_clamp)
|
||||
interpreter.SetProc("inrange", /proc/n_inrange)
|
||||
interpreter.SetProc("rand", /proc/rand_chance)
|
||||
// End of Donkie~
|
||||
|
||||
// Time
|
||||
interpreter.SetProc("time", /proc/time)
|
||||
interpreter.SetProc("timestamp", /proc/timestamp)
|
||||
|
||||
// Run the compiled code
|
||||
interpreter.Run()
|
||||
@@ -193,21 +214,16 @@
|
||||
// Backwards-apply variables onto signal data
|
||||
/* sanitize EVERYTHING. fucking players can't be trusted with SHIT */
|
||||
|
||||
signal.data["message"] = interpreter.GetVar("$content")
|
||||
signal.frequency = interpreter.GetVar("$freq")
|
||||
signal.data["message"] = interpreter.GetCleanVar("$content", signal.data["message"])
|
||||
signal.frequency = interpreter.GetCleanVar("$freq", signal.frequency)
|
||||
|
||||
var/setname = ""
|
||||
var/obj/machinery/telecomms/server/S = signal.data["server"]
|
||||
if(interpreter.GetVar("$source") in S.stored_names)
|
||||
setname = interpreter.GetVar("$source")
|
||||
else
|
||||
setname = "<i>[interpreter.GetVar("$source")]</i>"
|
||||
var/setname = interpreter.GetCleanVar("$source", signal.data["name"])
|
||||
|
||||
if(signal.data["name"] != setname)
|
||||
signal.data["realname"] = setname
|
||||
signal.data["name"] = setname
|
||||
signal.data["job"] = interpreter.GetVar("$job")
|
||||
signal.data["reject"] = !(interpreter.GetVar("$pass")) // set reject to the opposite of $pass
|
||||
signal.data["job"] = interpreter.GetCleanVar("$job", signal.data["job"])
|
||||
signal.data["reject"] = !(interpreter.GetCleanVar("$pass")) // set reject to the opposite of $pass
|
||||
|
||||
// If the message is invalid, just don't broadcast it!
|
||||
if(signal.data["message"] == "" || !signal.data["message"])
|
||||
@@ -215,6 +231,8 @@
|
||||
|
||||
/* -- Actual language proc code -- */
|
||||
|
||||
var/const/SIGNAL_COOLDOWN = 20 // 2 seconds
|
||||
|
||||
datum/signal
|
||||
|
||||
proc/mem(var/address, var/value)
|
||||
@@ -229,6 +247,37 @@ datum/signal
|
||||
S.memory[address] = value
|
||||
|
||||
|
||||
proc/signaler(var/freq = 1459, var/code = 30)
|
||||
|
||||
if(isnum(freq) && isnum(code))
|
||||
|
||||
var/obj/machinery/telecomms/server/S = data["server"]
|
||||
|
||||
if(S.last_signal + SIGNAL_COOLDOWN > world.timeofday && S.last_signal < MIDNIGHT_ROLLOVER)
|
||||
return
|
||||
S.last_signal = world.timeofday
|
||||
|
||||
var/datum/radio_frequency/connection = radio_controller.return_frequency(freq)
|
||||
|
||||
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
|
||||
freq *= 10 // shift the decimal one place
|
||||
|
||||
freq = sanitize_frequency(freq)
|
||||
|
||||
code = round(code)
|
||||
code = Clamp(code, 0, 100)
|
||||
|
||||
var/datum/signal/signal = new
|
||||
signal.source = S
|
||||
signal.encryption = code
|
||||
signal.data["message"] = "ACTIVATE"
|
||||
|
||||
connection.post_signal(S, signal)
|
||||
|
||||
var/time = time2text(world.realtime,"hh:mm:ss")
|
||||
lastsignalers.Add("[time] <B>:</B> [S.id] sent a signal command, which was triggered by NTSL.<B>:</B> [format_frequency(freq)]/[code]")
|
||||
|
||||
|
||||
proc/tcombroadcast(var/message, var/freq, var/source, var/job)
|
||||
|
||||
var/datum/signal/newsign = new
|
||||
@@ -244,7 +293,7 @@ datum/signal
|
||||
if(!source)
|
||||
source = "[html_encode(uppertext(S.id))]"
|
||||
hradio = new // sets the hradio as a radio intercom
|
||||
if(!freq)
|
||||
if(!freq || (!isnum(freq) && text2num(freq) == null))
|
||||
freq = 1459
|
||||
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
|
||||
freq *= 10 // shift the decimal one place
|
||||
@@ -254,12 +303,9 @@ datum/signal
|
||||
|
||||
newsign.data["mob"] = null
|
||||
newsign.data["mobtype"] = /mob/living/carbon/human
|
||||
if(source in S.stored_names)
|
||||
newsign.data["name"] = source
|
||||
else
|
||||
newsign.data["name"] = "<i>[html_encode(uppertext(source))]<i>"
|
||||
newsign.data["name"] = source
|
||||
newsign.data["realname"] = newsign.data["name"]
|
||||
newsign.data["job"] = job
|
||||
newsign.data["job"] = "[job]"
|
||||
newsign.data["compression"] = 0
|
||||
newsign.data["message"] = message
|
||||
newsign.data["type"] = 2 // artificial broadcast
|
||||
@@ -277,6 +323,8 @@ datum/signal
|
||||
newsign.data["vmask"] = 0
|
||||
newsign.data["level"] = list()
|
||||
|
||||
newsign.sanitize_data()
|
||||
|
||||
var/pass = S.relay_information(newsign, "/obj/machinery/telecomms/hub")
|
||||
if(!pass)
|
||||
S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters
|
||||
|
||||
@@ -94,6 +94,10 @@
|
||||
/proc/delay(var/time)
|
||||
sleep(time)
|
||||
|
||||
// Clone of rand()
|
||||
/proc/rand_chance(var/low = 0, var/high)
|
||||
return rand(low, high)
|
||||
|
||||
// Clone of prob()
|
||||
/proc/prob_chance(var/chance)
|
||||
return prob(chance)
|
||||
@@ -123,6 +127,7 @@
|
||||
if(container)
|
||||
if(istype(container, /list) || istext(container))
|
||||
return length(container)
|
||||
return 0
|
||||
|
||||
// BY DONKIE~
|
||||
// String stuff
|
||||
@@ -134,6 +139,12 @@
|
||||
if(istext(string))
|
||||
return uppertext(string)
|
||||
|
||||
/proc/time()
|
||||
return world.timeofday
|
||||
|
||||
/proc/timestamp(var/format = "hh:mm:ss") // Get the game time in text
|
||||
return time2text(world.time + 432000, format)
|
||||
|
||||
/*
|
||||
//Makes a list where all indicies in a string is a seperate index in the list
|
||||
// JUST A HELPER DON'T ADD TO NTSCRIPT
|
||||
@@ -165,8 +176,10 @@ proc/string_explode(var/string, var/separator)
|
||||
|
||||
Just found out there was already a string explode function, did some benchmarking, and that function were a bit faster, sticking to that.
|
||||
*/
|
||||
proc/string_explode(var/string, var/separator)
|
||||
if(istext(string) && istext(separator))
|
||||
|
||||
|
||||
proc/string_explode(var/string, var/separator = "")
|
||||
if(istext(string) && (istext(separator) || isnull(separator)))
|
||||
return text2list(string, separator)
|
||||
|
||||
proc/n_repeat(var/string, var/amount)
|
||||
@@ -247,15 +260,18 @@ proc/n_inrange(var/num, var/min=-1, var/max=1)
|
||||
|
||||
// Non-recursive
|
||||
// Imported from Mono string.ReplaceUnchecked
|
||||
/*
|
||||
/proc/string_replacetext(var/haystack,var/a,var/b)
|
||||
if(istext(haystack)&&istext(a)&&istext(b))
|
||||
var/i = 1
|
||||
var/lenh=lentext(haystack)
|
||||
var/lena=lentext(a)
|
||||
//var/lenb=lentext(b)
|
||||
var/count = 0
|
||||
var/list/dat = list()
|
||||
while (i < lenh)
|
||||
var/found = findtext(haystack, a, i, 0)
|
||||
//diary << "findtext([haystack], [a], [i], 0)=[found]"
|
||||
if (found == 0) // Not found
|
||||
break
|
||||
else
|
||||
@@ -263,50 +279,49 @@ proc/n_inrange(var/num, var/min=-1, var/max=1)
|
||||
dat+=found
|
||||
count+=1
|
||||
else
|
||||
//diary << "Script found [a] [count] times, aborted"
|
||||
break
|
||||
//diary << "Found [a] at [found]! Moving up..."
|
||||
i = found + lena
|
||||
if (count == 0)
|
||||
return haystack
|
||||
//var/nlen = lenh + ((lenb - lena) * count)
|
||||
var/buf = copytext(haystack,1,dat[1]) // Prefill
|
||||
var/lastReadPos = 0
|
||||
for (i = 1, i <= count, i++)
|
||||
var/precopy = dat[i] - lastReadPos-1
|
||||
//internal static unsafe void CharCopy (String target, int targetIndex, String source, int sourceIndex, int count)
|
||||
//fixed (char* dest = target, src = source)
|
||||
//CharCopy (dest + targetIndex, src + sourceIndex, count);
|
||||
//CharCopy (dest + curPos, source + lastReadPos, precopy);
|
||||
buf+=copytext(haystack,lastReadPos,precopy)
|
||||
diary << "buf+=copytext([haystack],[lastReadPos],[precopy])"
|
||||
diary<<"[buf]"
|
||||
lastReadPos = dat[i] + lena
|
||||
//CharCopy (dest + curPos, replace, newValue.length);
|
||||
buf+=b
|
||||
diary<<"[buf]"
|
||||
buf+=copytext(haystack,lastReadPos, 0)
|
||||
return buf
|
||||
*/
|
||||
|
||||
|
||||
/proc/ntsl_send_signal(var/freq,var/code)
|
||||
if(isnum(freq)&&isnum(code))
|
||||
if(!radio_controller)
|
||||
sleep(20)
|
||||
if(!radio_controller)
|
||||
/proc/string_replacetext(text, find, replacement)
|
||||
if(istext(text) && istext(find) && istext(replacement))
|
||||
var/find_len = length(find)
|
||||
if(find_len < 1) return text
|
||||
. = ""
|
||||
var/last_found = 1
|
||||
var/count = 0
|
||||
while(1)
|
||||
count += 1
|
||||
if(count > SCRIPT_MAX_REPLACEMENTS_ALLOWED)
|
||||
break
|
||||
var/found = findtext(text, find, last_found, 0)
|
||||
. += copytext(text, last_found, found)
|
||||
if(found)
|
||||
. += replacement
|
||||
last_found = found + find_len
|
||||
continue
|
||||
return
|
||||
// Sanitize frequency
|
||||
var/new_frequency = freq
|
||||
if(new_frequency < 1200 || new_frequency > 1600)
|
||||
new_frequency = sanitize_frequency(new_frequency)
|
||||
|
||||
// Sanitize code
|
||||
code = round(code)
|
||||
code = min(100, code)
|
||||
code = max(1, code)
|
||||
|
||||
// Since we're not an object, we can't do this.
|
||||
/*
|
||||
radio_controller.remove_object(src, frequency)
|
||||
frequency = new_frequency
|
||||
radio_connection = radio_controller.add_object(src, frequency, RADIO_CHAT)
|
||||
*/
|
||||
|
||||
// Therefore, we do this hacky bit of crap instead.
|
||||
var/datum/radio_frequency/radio_connection = radio_controller.return_frequency(freq)
|
||||
|
||||
// Go go gadget
|
||||
var/datum/signal/signal = new
|
||||
signal.source = null // Oh god I hope this works.
|
||||
signal.encryption = code
|
||||
signal.data["message"] = "ACTIVATE"
|
||||
radio_connection.post_signal(null, signal)
|
||||
#undef SCRIPT_MAX_REPLACEMENTS_ALLOWED
|
||||
@@ -21,6 +21,7 @@
|
||||
ASSERT(program)
|
||||
src.program = program
|
||||
CreateGlobalScope()
|
||||
alertadmins = 0 // reset admin alerts
|
||||
|
||||
/*
|
||||
Proc: Run
|
||||
@@ -29,7 +30,6 @@
|
||||
Run()
|
||||
cur_recursion = 0 // reset recursion
|
||||
cur_statements = 0 // reset CPU tracking
|
||||
alertadmins = 0
|
||||
|
||||
ASSERT(src.program)
|
||||
RunBlock(src.program)
|
||||
@@ -110,6 +110,17 @@
|
||||
var/x = globalScope.variables[name]
|
||||
return Eval(x)
|
||||
|
||||
/*
|
||||
Proc: GetCleanVar
|
||||
Returns the value of a global variable in the script and cleans it (sanitizes).
|
||||
*/
|
||||
|
||||
GetCleanVar(name, compare)
|
||||
var/x = GetVar(name)
|
||||
if(istext(x) && compare && x != compare) // Was changed
|
||||
x = sanitize(x)
|
||||
return x
|
||||
|
||||
/*
|
||||
Proc: CallProc
|
||||
Calls a global function defined in the script and, amazingly enough, returns its return value. Remember to ensure that the function
|
||||
|
||||
@@ -33,11 +33,11 @@
|
||||
status=0
|
||||
returnVal
|
||||
|
||||
max_statements=1000 // maximum amount of statements that can be called in one execution. this is to prevent massive crashes and exploitation
|
||||
max_statements=900 // maximum amount of statements that can be called in one execution. this is to prevent massive crashes and exploitation
|
||||
cur_statements=0 // current amount of statements called
|
||||
alertadmins=0 // set to 1 if the admins shouldn't be notified of anymore issues
|
||||
max_iterations=100 // max number of uninterrupted loops possible
|
||||
max_recursion=50 // max recursions without returning anything (or completing the code block)
|
||||
max_recursion=10 // max recursions without returning anything (or completing the code block)
|
||||
cur_recursion=0 // current amount of recursion
|
||||
/*
|
||||
Var: persist
|
||||
@@ -55,6 +55,14 @@
|
||||
if(program)Load(program)
|
||||
|
||||
proc
|
||||
|
||||
/*
|
||||
Set ourselves to Garbage Collect
|
||||
*/
|
||||
GC()
|
||||
..()
|
||||
container = null
|
||||
|
||||
/*
|
||||
Proc: RaiseError
|
||||
Raises a runtime error.
|
||||
@@ -76,6 +84,19 @@
|
||||
globalScope = S
|
||||
return S
|
||||
|
||||
/*
|
||||
Proc: AlertAdmins
|
||||
Alerts the admins of a script that is bad.
|
||||
*/
|
||||
AlertAdmins()
|
||||
if(container && !alertadmins)
|
||||
if(istype(container, /datum/TCS_Compiler))
|
||||
var/datum/TCS_Compiler/Compiler = container
|
||||
var/obj/machinery/telecomms/server/Holder = Compiler.Holder
|
||||
var/message = "Potential crash-inducing NTSL script detected at telecommunications server [Compiler.Holder] ([Holder.x], [Holder.y], [Holder.z])."
|
||||
|
||||
alertadmins = 1
|
||||
message_admins(message, 1)
|
||||
/*
|
||||
Proc: RunBlock
|
||||
Runs each statement in a block of code.
|
||||
@@ -100,15 +121,7 @@
|
||||
cur_statements++
|
||||
if(cur_statements >= max_statements)
|
||||
RaiseError(new/runtimeError/MaxCPU())
|
||||
|
||||
if(container && !alertadmins)
|
||||
if(istype(container, /datum/TCS_Compiler))
|
||||
var/datum/TCS_Compiler/Compiler = container
|
||||
var/obj/machinery/telecomms/server/Holder = Compiler.Holder
|
||||
var/message = "Potential crash-inducing NTSL script detected at telecommunications server [Compiler.Holder] ([Holder.x], [Holder.y], [Holder.z])."
|
||||
|
||||
alertadmins = 1
|
||||
message_admins(message, 1)
|
||||
AlertAdmins()
|
||||
break
|
||||
|
||||
if(istype(S, /node/statement/VariableAssignment))
|
||||
@@ -171,6 +184,7 @@
|
||||
|
||||
// If recursion gets too high (max 50 nested functions) throw an error
|
||||
if(cur_recursion >= max_recursion)
|
||||
AlertAdmins()
|
||||
RaiseError(new/runtimeError/RecursionLimitReached())
|
||||
return 0
|
||||
|
||||
@@ -223,10 +237,21 @@
|
||||
Checks a condition and runs either the if block or else block.
|
||||
*/
|
||||
RunIf(node/statement/IfStatement/stmt)
|
||||
if(Eval(stmt.cond))
|
||||
RunBlock(stmt.block)
|
||||
else if(stmt.else_block)
|
||||
RunBlock(stmt.else_block)
|
||||
if(!stmt.skip)
|
||||
if(Eval(stmt.cond))
|
||||
RunBlock(stmt.block)
|
||||
// Loop through the if else chain and tell them to be skipped.
|
||||
var/node/statement/IfStatement/i = stmt.else_if
|
||||
var/fail_safe = 800
|
||||
while(i && fail_safe)
|
||||
fail_safe -= 1
|
||||
i.skip = 1
|
||||
i = i.else_if
|
||||
|
||||
else if(stmt.else_block)
|
||||
RunBlock(stmt.else_block)
|
||||
// We don't need to skip you anymore.
|
||||
stmt.skip = 0
|
||||
|
||||
/*
|
||||
Proc: RunWhile
|
||||
@@ -312,3 +337,4 @@
|
||||
//TODO: check for invalid name
|
||||
S.variables["[name]"] = value
|
||||
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ n_scriptOptions
|
||||
An associative list used by the parser to parse keywords. Indices are strings which will trigger the keyword when parsed and the
|
||||
associated values are <nS_Keyword> types of which the <n_Keyword.Parse()> proc will be called.
|
||||
*/
|
||||
keywords = list("if" = /n_Keyword/nS_Keyword/kwIf, "else" = /n_Keyword/nS_Keyword/kwElse, \
|
||||
keywords = list("if" = /n_Keyword/nS_Keyword/kwIf, "else" = /n_Keyword/nS_Keyword/kwElse, "elseif" = /n_Keyword/nS_Keyword/kwElseIf, \
|
||||
"while" = /n_Keyword/nS_Keyword/kwWhile, "break" = /n_Keyword/nS_Keyword/kwBreak, \
|
||||
"continue" = /n_Keyword/nS_Keyword/kwContinue, \
|
||||
"return" = /n_Keyword/nS_Keyword/kwReturn, "def" = /n_Keyword/nS_Keyword/kwDef)
|
||||
|
||||
@@ -180,12 +180,18 @@
|
||||
- <ParseParenExpression()>
|
||||
- <ParseParamExpression()>
|
||||
*/
|
||||
ParseExpression(list/end=list(/token/end), list/ErrChars=list("{", "}"))
|
||||
ParseExpression(list/end=list(/token/end), list/ErrChars=list("{", "}"), check_functions = 0)
|
||||
var/stack
|
||||
opr=new
|
||||
val=new
|
||||
src.expecting=VALUE
|
||||
var/loop = 0
|
||||
for()
|
||||
loop++
|
||||
if(loop > 800)
|
||||
errors+=new/scriptError("Too many nested tokens.")
|
||||
return
|
||||
|
||||
if(EndOfExpression(end))
|
||||
break
|
||||
if(istype(curToken, /token/symbol) && ErrChars.Find(curToken.value))
|
||||
@@ -206,6 +212,7 @@
|
||||
NextToken()
|
||||
continue
|
||||
val.Push(ParseParenExpression())
|
||||
|
||||
else if(istype(curToken, /token/symbol)) //Operator found.
|
||||
var/node/expression/operator/curOperator //Figure out whether it is unary or binary and get a new instance.
|
||||
if(src.expecting==OPERATOR)
|
||||
@@ -226,16 +233,24 @@
|
||||
continue
|
||||
opr.Push(curOperator)
|
||||
src.expecting=VALUE
|
||||
|
||||
else if(ntok && ntok.value=="(" && istype(ntok, /token/symbol)\
|
||||
&& istype(curToken, /token/word)) //Parse function call
|
||||
var/token/preToken=curToken
|
||||
var/old_expect=src.expecting
|
||||
var/fex=ParseFunctionExpression()
|
||||
if(old_expect!=VALUE)
|
||||
errors+=new/scriptError/ExpectedToken("operator", preToken)
|
||||
NextToken()
|
||||
continue
|
||||
val.Push(fex)
|
||||
|
||||
if(!check_functions)
|
||||
|
||||
var/token/preToken=curToken
|
||||
var/old_expect=src.expecting
|
||||
var/fex=ParseFunctionExpression()
|
||||
if(old_expect!=VALUE)
|
||||
errors+=new/scriptError/ExpectedToken("operator", preToken)
|
||||
NextToken()
|
||||
continue
|
||||
val.Push(fex)
|
||||
else
|
||||
errors+=new/scriptError/ParameterFunction(curToken)
|
||||
break
|
||||
|
||||
else if(istype(curToken, /token/keyword)) //inline keywords
|
||||
var/n_Keyword/kw=options.keywords[curToken.value]
|
||||
kw=new kw(inline=1)
|
||||
@@ -244,6 +259,7 @@
|
||||
return
|
||||
else
|
||||
errors+=new/scriptError/BadToken(curToken)
|
||||
|
||||
else if(istype(curToken, /token/end)) //semicolon found where it wasn't expected
|
||||
errors+=new/scriptError/BadToken(curToken)
|
||||
NextToken()
|
||||
@@ -255,6 +271,7 @@
|
||||
continue
|
||||
val.Push(GetExpression(curToken))
|
||||
src.expecting=OPERATOR
|
||||
|
||||
NextToken()
|
||||
|
||||
while(opr.Top()) Reduce(opr, val) //Reduce the value stack completely
|
||||
@@ -280,12 +297,16 @@
|
||||
|
||||
for()
|
||||
loops++
|
||||
if(loops>=1000)
|
||||
CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
|
||||
if(loops>=800)
|
||||
errors += new/scriptError("Too many nested expressions.")
|
||||
break
|
||||
//CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
|
||||
|
||||
if(istype(curToken, /token/symbol) && curToken.value==")")
|
||||
return exp
|
||||
exp.parameters+=ParseParamExpression()
|
||||
if(errors.len)
|
||||
return exp
|
||||
if(curToken.value==","&&istype(curToken, /token/symbol))NextToken() //skip comma
|
||||
if(istype(curToken, /token/end)) //Prevents infinite loop...
|
||||
errors+=new/scriptError/ExpectedToken(")")
|
||||
@@ -310,5 +331,6 @@
|
||||
See Also:
|
||||
- <ParseExpression()>
|
||||
*/
|
||||
ParseParamExpression()
|
||||
return ParseExpression(list(",", ")"))
|
||||
ParseParamExpression(var/check_functions = 0)
|
||||
var/cf = check_functions
|
||||
return ParseExpression(list(",", ")"), check_functions = cf)
|
||||
@@ -51,9 +51,9 @@ var/const/Represents a special statement in the code triggered by a keyword.
|
||||
kwReturn
|
||||
Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock))
|
||||
parser.errors+=new/scriptError/BadReturn(parser.curToken)
|
||||
. = KW_WARN
|
||||
if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock)) // Exit out of the program by setting the tokens list size to the same as index.
|
||||
parser.tokens.len = parser.index
|
||||
return
|
||||
var/node/statement/ReturnStatement/stmt=new
|
||||
parser.NextToken() //skip 'return' token
|
||||
stmt.value=parser.ParseExpression()
|
||||
@@ -73,6 +73,31 @@ var/const/Represents a special statement in the code triggered by a keyword.
|
||||
stmt.block=new
|
||||
parser.AddBlock(stmt.block)
|
||||
|
||||
kwElseIf
|
||||
Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
var/list/L=parser.curBlock.statements
|
||||
var/node/statement/IfStatement/ifstmt
|
||||
|
||||
if(L && L.len)
|
||||
ifstmt = L[L.len] //Get the last statement in the current block
|
||||
if(!ifstmt || !istype(ifstmt) || ifstmt.else_if)
|
||||
parser.errors += new/scriptError/ExpectedToken("if statement", parser.curToken)
|
||||
return KW_FAIL
|
||||
|
||||
var/node/statement/IfStatement/ElseIf/stmt = new
|
||||
parser.NextToken() //skip 'if' token
|
||||
stmt.cond = parser.ParseParenExpression()
|
||||
if(!parser.CheckToken(")", /token/symbol))
|
||||
return KW_FAIL
|
||||
if(!parser.CheckToken("{", /token/symbol, skip=0)) //Token needs to be preserved for parse loop, so skip=0
|
||||
return KW_ERR
|
||||
parser.curBlock.statements+=stmt
|
||||
stmt.block=new
|
||||
ifstmt.else_if = stmt
|
||||
parser.AddBlock(stmt.block)
|
||||
|
||||
|
||||
kwElse
|
||||
Parse(n_Parser/nS_Parser/parser)
|
||||
.=KW_PASS
|
||||
|
||||
@@ -174,8 +174,9 @@
|
||||
var/loops = 0
|
||||
for()
|
||||
loops++
|
||||
if(loops>=6000)
|
||||
CRASH("Something TERRIBLE has gone wrong in ParseFunctionStatement ;__;")
|
||||
if(loops>=800)
|
||||
errors +=new/scriptError("Cannot find ending params.")
|
||||
return
|
||||
|
||||
if(!curToken)
|
||||
errors+=new/scriptError/EndOfFile()
|
||||
@@ -184,6 +185,6 @@
|
||||
curBlock.statements+=stmt
|
||||
NextToken() //Skip close parenthesis
|
||||
return
|
||||
var/node/expression/P=ParseParamExpression()
|
||||
var/node/expression/P=ParseParamExpression(check_functions = 1)
|
||||
stmt.parameters+=P
|
||||
if(istype(curToken, /token/symbol) && curToken.value==",") NextToken()
|
||||
Reference in New Issue
Block a user