Update NTSL from /tg/.

This commit is contained in:
Rob Nelson
2013-08-14 15:42:23 -07:00
parent 3fab835417
commit 153871400a
16 changed files with 603 additions and 361 deletions

View File

@@ -226,6 +226,12 @@ var/message_delay = 0 // To make sure restarting the recentmessages list is kept
var/list/obj/item/device/radio/radios = list() var/list/obj/item/device/radio/radios = list()
// Cut down on the message sizes.
message = copytext(message, 1, MAX_BROADCAST_LEN)
vmessage = copytext(vmessage, 1, MAX_BROADCAST_LEN)
// --- Broadcast only to intercom devices --- // --- Broadcast only to intercom devices ---
if(data == 1) if(data == 1)

View File

@@ -59,8 +59,10 @@
del(A) del(A)
construct_op -- construct_op --
stat &= ~BROKEN // the machine's not borked anymore! stat &= ~BROKEN // the machine's not borked anymore!
else
user << "You need more cable"
if(istype(P, /obj/item/weapon/crowbar)) if(istype(P, /obj/item/weapon/crowbar))
user << "You begin prying out the circuit board other components..." user << "You begin prying out the circuit board and components..."
playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1) playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1)
if(do_after(user,60)) if(do_after(user,60))
user << "You finish prying out the components." user << "You finish prying out the components."
@@ -275,14 +277,13 @@
/obj/machinery/telecomms/Topic(href, href_list) /obj/machinery/telecomms/Topic(href, href_list)
if(..())
return
if(!issilicon(usr)) if(!issilicon(usr))
if(!istype(usr.get_active_hand(), /obj/item/device/multitool)) if(!istype(usr.get_active_hand(), /obj/item/device/multitool))
return return
if(stat & (BROKEN|NOPOWER))
return
var/obj/item/device/multitool/P = get_multitool(usr) var/obj/item/device/multitool/P = get_multitool(usr)
if(href_list["input"]) if(href_list["input"])
@@ -343,13 +344,17 @@
if(text2num(href_list["unlink"]) <= length(links)) if(text2num(href_list["unlink"]) <= length(links))
var/obj/machinery/telecomms/T = links[text2num(href_list["unlink"])] var/obj/machinery/telecomms/T = links[text2num(href_list["unlink"])]
temp = "<font color = #666633>-% Removed \ref[T] [T.name] from linked entities. %-</font color>" if(T)
temp = "<font color = #666633>-% Removed \ref[T] [T.name] from linked entities. %-</font color>"
// Remove link entries from both T and src. // Remove link entries from both T and src.
if(src in T.links) if(T.links)
T.links.Remove(src) T.links.Remove(src)
links.Remove(T) links.Remove(T)
else
temp = "<font color = #666633>-% Unable to locate machine to unlink from, try again. %-</font color>"
if(href_list["link"]) if(href_list["link"])
@@ -380,7 +385,6 @@
src.Options_Topic(href, href_list) src.Options_Topic(href, href_list)
usr.set_machine(src) usr.set_machine(src)
src.add_fingerprint(usr)
updateUsrDialog() updateUsrDialog()

View File

@@ -125,9 +125,13 @@
//Servers //Servers
/obj/machinery/telecomms/server/presets /obj/machinery/telecomms/server/presets
network = "tcommsat" network = "tcommsat"
/obj/machinery/telecomms/server/presets/New()
..()
name = id
/obj/machinery/telecomms/server/presets/science /obj/machinery/telecomms/server/presets/science
id = "Science Server" id = "Science Server"
freq_listening = list(1351) freq_listening = list(1351)

View File

@@ -101,7 +101,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
copy.data["original"] = signal.data["original"] copy.data["original"] = signal.data["original"]
else else
del(copy) copy = null
send_count++ send_count++
@@ -167,10 +167,11 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
var/turf/position = get_turf(src) var/turf/position = get_turf(src)
var/turf/T_position = get_turf(T) var/turf/T_position = get_turf(T)
if((position.z == T_position.z) || (src.long_range_link && T.long_range_link)) if((position.z == T_position.z) || (src.long_range_link && T.long_range_link))
for(var/x in autolinkers) if(src != T)
if(T.autolinkers.Find(x)) for(var/x in autolinkers)
if(src != T) if(x in T.autolinkers)
links |= T links |= T
break
/obj/machinery/telecomms/update_icon() /obj/machinery/telecomms/update_icon()
if(on) if(on)
@@ -519,6 +520,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
// would add up to md5("password123comsat") // would add up to md5("password123comsat")
var/language = "human" var/language = "human"
var/obj/item/device/radio/headset/server_radio = null var/obj/item/device/radio/headset/server_radio = null
var/last_signal = 0 // Last time it sent a signal
/obj/machinery/telecomms/server/New() /obj/machinery/telecomms/server/New()
..() ..()
@@ -526,6 +528,13 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
Compiler.Holder = src Compiler.Holder = src
server_radio = new() server_radio = new()
/obj/machinery/telecomms/server/Del()
// Garbage collects all the NTSL datums.
if(Compiler)
Compiler.GC()
Compiler = null
..()
/obj/machinery/telecomms/server/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from) /obj/machinery/telecomms/server/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
if(signal.data["message"]) if(signal.data["message"])
@@ -593,8 +602,19 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list()
if(istext(t)) if(istext(t))
rawcode = t rawcode = t
/obj/machinery/telecomms/server/proc/compile() /obj/machinery/telecomms/server/proc/admin_log(var/mob/mob)
var/msg="[mob.name] has compiled a script to server [src]:"
diary << msg
diary << rawcode
src.investigate_log("[msg]<br>[rawcode]", "ntsl")
if(length(rawcode)) // Let's not bother the admins for empty code.
message_admins("[mob.real_name] ([mob.key]) has compiled and uploaded a NTLS script to [src.id] ([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)
/obj/machinery/telecomms/server/proc/compile(var/mob/user)
if(Compiler) if(Compiler)
admin_log(user)
return Compiler.Compile(rawcode) return Compiler.Compile(rawcode)
/obj/machinery/telecomms/server/proc/update_logs() /obj/machinery/telecomms/server/proc/update_logs()

View File

@@ -19,62 +19,81 @@
var/temp = "" // temporary feedback messages var/temp = "" // temporary feedback messages
var/storedcode = "" // code stored var/storedcode = "" // code stored
var/obj/item/weapon/card/id/auth = null
var/list/access_log = list()
proc/update_ide() var/process = 0
// loop if there's someone manning the keyboard
while(editingcode)
if(!editingcode.client)
editingcode = null
break
// For the typer, the input is enabled. Buffer the typed text
if(editingcode)
storedcode = "[winget(editingcode, "tcscode", "text")]"
if(editingcode) // double if's to work around a runtime error
winset(editingcode, "tcscode", "is-disabled=false")
// If the player's not manning the keyboard anymore, adjust everything
if( (!(editingcode in range(1, src)) && !issilicon(editingcode)) || (editingcode.machine != src && !issilicon(editingcode)))
if(editingcode)
winshow(editingcode, "Telecomms IDE", 0) // hide the window!
editingcode = null
break
// For other people viewing the typer type code, the input is disabled and they can only view the code
// (this is put in place so that there's not any magical shenanigans with 50 people inputting different code all at once)
if(length(viewingcode))
// This piece of code is very important - it escapes quotation marks so string aren't cut off by the input element
var/showcode = replacetext(storedcode, "\\\"", "\\\\\"")
showcode = replacetext(storedcode, "\"", "\\\"")
for(var/mob/M in viewingcode)
if( (M.machine == src && M in view(1, src) ) || issilicon(M))
winset(M, "tcscode", "is-disabled=true")
winset(M, "tcscode", "text=\"[showcode]\"")
else
viewingcode.Remove(M)
winshow(M, "Telecomms IDE", 0) // hide the window!
sleep(5)
if(length(viewingcode) > 0)
editingcode = pick(viewingcode)
viewingcode.Remove(editingcode)
update_ide()
req_access = list(access_tcomsat) req_access = list(access_tcomsat)
attack_hand(mob/user as mob) /obj/machinery/computer/telecomms/traffic/proc/stop_editing()
if(stat & (BROKEN|NOPOWER)) if(editingcode)
return if(editingcode.client)
user.set_machine(src) winshow(editingcode, "Telecomms IDE", 0) // hide the window!
var/dat = "<TITLE>Telecommunication Traffic Control</TITLE><center><b>Telecommunications Traffic Control</b></center>" editingcode.unset_machine()
editingcode = null
/obj/machinery/computer/telecomms/traffic/process()
if(stat & (NOPOWER|BROKEN))
stop_editing()
return
if(editingcode && editingcode.machine != src)
stop_editing()
return
if(!editingcode)
if(length(viewingcode) > 0)
editingcode = pick(viewingcode)
viewingcode.Remove(editingcode)
return
process = !process
if(!process)
return
// loop if there's someone manning the keyboard
if(!editingcode.client)
stop_editing()
return
// For the typer, the input is enabled. Buffer the typed text
storedcode = "[winget(editingcode, "tcscode", "text")]"
winset(editingcode, "tcscode", "is-disabled=false")
// If the player's not manning the keyboard anymore, adjust everything
if(!in_range(editingcode, src) && !issilicon(editingcode) || editingcode.machine != src)
winshow(editingcode, "Telecomms IDE", 0) // hide the window!
editingcode = null
return
// For other people viewing the typer type code, the input is disabled and they can only view the code
// (this is put in place so that there's not any magical shenanigans with 50 people inputting different code all at once)
if(length(viewingcode))
// This piece of code is very important - it escapes quotation marks so string aren't cut off by the input element
var/showcode = replacetext(storedcode, "\\\"", "\\\\\"")
showcode = replacetext(storedcode, "\"", "\\\"")
for(var/mob/M in viewingcode)
if( (M.machine == src && in_range(M, src) ) || issilicon(M))
winset(M, "tcscode", "is-disabled=true")
winset(M, "tcscode", "text=\"[showcode]\"")
else
viewingcode.Remove(M)
winshow(M, "Telecomms IDE", 0) // hide the windows
/obj/machinery/computer/telecomms/traffic/attack_hand(mob/user as mob)
if(stat & (BROKEN|NOPOWER))
return
user.set_machine(src)
var/dat = "<TITLE>Telecommunication Traffic Control</TITLE><center><b>Telecommunications Traffic Control</b></center>"
dat += "<br><b><font color='[(auth ? "green" : "red")]'>[(auth ? "AUTHED" : "NOT AUTHED")]:</font></b> <A href='?src=\ref[src];auth=1'>[(!auth ? "Insert ID" : auth.registered_name)]</A><BR>"
dat += "<A href='?src=\ref[src];print=1'>View System Log</A><HR>"
if(issilicon(user) || auth)
switch(screen) switch(screen)
@@ -98,145 +117,196 @@
// --- Viewing Server --- // --- Viewing Server ---
if(1) if(1)
dat += "<br>[temp]<br>" if(SelectedServer)
dat += "<center><a href='?src=\ref[src];operation=mainmenu'>\[Main Menu\]</a> <a href='?src=\ref[src];operation=refresh'>\[Refresh\]</a></center>" dat += "<br>[temp]<br>"
dat += "<br>Current Network: [network]" dat += "<center><a href='?src=\ref[src];operation=mainmenu'>\[Main Menu\]</a> <a href='?src=\ref[src];operation=refresh'>\[Refresh\]</a></center>"
dat += "<br>Selected Server: [SelectedServer.id]<br><br>" dat += "<br>Current Network: [network]"
dat += "<br><a href='?src=\ref[src];operation=editcode'>\[Edit Code\]</a>" dat += "<br>Selected Server: [SelectedServer.id]<br><br>"
dat += "<br>Signal Execution: " dat += "<br><a href='?src=\ref[src];operation=editcode'>\[Edit Code\]</a>"
if(SelectedServer.autoruncode) dat += "<br>Signal Execution: "
dat += "<a href='?src=\ref[src];operation=togglerun'>ALWAYS</a>" if(SelectedServer.autoruncode)
dat += "<a href='?src=\ref[src];operation=togglerun'>ALWAYS</a>"
else
dat += "<a href='?src=\ref[src];operation=togglerun'>NEVER</a>"
else else
dat += "<a href='?src=\ref[src];operation=togglerun'>NEVER</a>" screen = 0
return
user << browse(dat, "window=traffic_control;size=575x400") user << browse(dat, "window=traffic_control;size=575x400")
onclose(user, "server_control") onclose(user, "server_control")
temp = "" temp = ""
return
/obj/machinery/computer/telecomms/traffic/proc/create_log(var/entry, var/mob/user)
var/id = null
if(issilicon(user))
id = "System Administrator"
else
if(auth)
id = "[auth.registered_name] ([auth.assignment])"
else
error("There is a null auth while the user isn't a silicon! ([user.name], [user.type])")
return
access_log += "\[[get_timestamp()]\] [id] [entry]"
/obj/machinery/computer/telecomms/traffic/proc/print_logs()
. = "<center><h2>Traffic Control Telecomms System Log</h2></center><HR>"
for(var/entry in access_log)
. += entry + "<BR>"
return .
/obj/machinery/computer/telecomms/traffic/Topic(href, href_list)
if(..())
return return
Topic(href, href_list) add_fingerprint(usr)
if(..()) usr.set_machine(src)
if(href_list["auth"])
if(iscarbon(usr))
var/mob/living/carbon/C = usr
if(!auth)
var/obj/item/weapon/card/id/I = C.get_active_hand()
if(istype(I))
if(check_access(I))
C.drop_item()
I.loc = src
auth = I
create_log("has logged in.", usr)
else
create_log("has logged out.", usr)
auth.loc = src.loc
C.put_in_hands(auth)
auth = null
updateUsrDialog()
return return
if(href_list["print"])
add_fingerprint(usr) usr << browse(print_logs(), "window=traffic_logs")
usr.set_machine(src)
if(!src.allowed(usr) && !emagged)
usr << "\red ACCESS DENIED."
return
if(href_list["viewserver"])
screen = 1
for(var/obj/machinery/telecomms/T in servers)
if(T.id == href_list["viewserver"])
SelectedServer = T
break
if(href_list["operation"])
switch(href_list["operation"])
if("release")
servers = list()
screen = 0
if("mainmenu")
screen = 0
if("scan")
if(servers.len > 0)
temp = "<font color = #D70B00>- FAILED: CANNOT PROBE WHEN BUFFER FULL -</font color>"
else
for(var/obj/machinery/telecomms/server/T in range(25, src))
if(T.network == network)
servers.Add(T)
if(!servers.len)
temp = "<font color = #D70B00>- FAILED: UNABLE TO LOCATE SERVERS IN \[[network]\] -</font color>"
else
temp = "<font color = #336699>- [servers.len] SERVERS PROBED & BUFFERED -</font color>"
screen = 0
if("editcode")
if(editingcode == usr) return
if(usr in viewingcode) return
if(!editingcode)
lasteditor = usr
editingcode = usr
winshow(editingcode, "Telecomms IDE", 1) // show the IDE
winset(editingcode, "tcscode", "is-disabled=false")
winset(editingcode, "tcscode", "text=\"\"")
var/showcode = replacetext(storedcode, "\\\"", "\\\\\"")
showcode = replacetext(storedcode, "\"", "\\\"")
winset(editingcode, "tcscode", "text=\"[showcode]\"")
spawn()
update_ide()
else
viewingcode.Add(usr)
winshow(usr, "Telecomms IDE", 1) // show the IDE
winset(usr, "tcscode", "is-disabled=true")
winset(editingcode, "tcscode", "text=\"\"")
var/showcode = replacetext(storedcode, "\"", "\\\"")
winset(usr, "tcscode", "text=\"[showcode]\"")
if("togglerun")
SelectedServer.autoruncode = !(SelectedServer.autoruncode)
if(href_list["network"])
// Haha what
//world.log << "diddly widdly scrub fuck nigger [issilicon(usr)] [istype(usr, /mob/living/silicon)]"
var/newnet = input(usr, "Which network do you want to view?", "Comm Monitor", network) as null|text
if(newnet && ((usr in range(1, src) || issilicon(usr))))
if(length(newnet) > 15)
temp = "<font color = #D70B00>- FAILED: NETWORK TAG STRING TOO LENGHTLY -</font color>"
else
network = newnet
screen = 0
servers = list()
temp = "<font color = #336699>- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -</font color>"
updateUsrDialog()
return return
attackby(var/obj/item/weapon/D as obj, var/mob/user as mob) if(!auth && !issilicon(usr) && !emagged)
if(istype(D, /obj/item/weapon/screwdriver)) usr << "\red ACCESS DENIED."
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
if(do_after(user, 20))
if (src.stat & BROKEN)
user << "\blue The broken glass falls out."
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
new /obj/item/weapon/shard( src.loc )
var/obj/item/weapon/circuitboard/comm_traffic/M = new /obj/item/weapon/circuitboard/comm_traffic( A )
for (var/obj/C in src)
C.loc = src.loc
A.circuit = M
A.state = 3
A.icon_state = "3"
A.anchored = 1
del(src)
else
user << "\blue You disconnect the monitor."
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
var/obj/item/weapon/circuitboard/comm_traffic/M = new /obj/item/weapon/circuitboard/comm_traffic( A )
for (var/obj/C in src)
C.loc = src.loc
A.circuit = M
A.state = 4
A.icon_state = "4"
A.anchored = 1
del(src)
else if(istype(D, /obj/item/weapon/card/emag) && !emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
user << "\blue You you disable the security protocols"
src.updateUsrDialog()
return return
if(href_list["viewserver"])
screen = 1
for(var/obj/machinery/telecomms/T in servers)
if(T.id == href_list["viewserver"])
SelectedServer = T
create_log("selected server [T.name]", usr)
break
if(href_list["operation"])
create_log("has performed action: [href_list["operation"]].", usr)
switch(href_list["operation"])
if("release")
servers = list()
screen = 0
if("mainmenu")
screen = 0
if("scan")
if(servers.len > 0)
temp = "<font color = #D70B00>- FAILED: CANNOT PROBE WHEN BUFFER FULL -</font color>"
else
for(var/obj/machinery/telecomms/server/T in range(25, src))
if(T.network == network)
servers.Add(T)
if(!servers.len)
temp = "<font color = #D70B00>- FAILED: UNABLE TO LOCATE SERVERS IN \[[network]\] -</font color>"
else
temp = "<font color = #336699>- [servers.len] SERVERS PROBED & BUFFERED -</font color>"
screen = 0
if("editcode")
if(editingcode == usr) return
if(usr in viewingcode) return
if(!editingcode)
lasteditor = usr
editingcode = usr
winshow(editingcode, "Telecomms IDE", 1) // show the IDE
winset(editingcode, "tcscode", "is-disabled=false")
winset(editingcode, "tcscode", "text=\"\"")
var/showcode = replacetext(storedcode, "\\\"", "\\\\\"")
showcode = replacetext(storedcode, "\"", "\\\"")
winset(editingcode, "tcscode", "text=\"[showcode]\"")
else
viewingcode.Add(usr)
winshow(usr, "Telecomms IDE", 1) // show the IDE
winset(usr, "tcscode", "is-disabled=true")
winset(editingcode, "tcscode", "text=\"\"")
var/showcode = replacetext(storedcode, "\"", "\\\"")
winset(usr, "tcscode", "text=\"[showcode]\"")
if("togglerun")
SelectedServer.autoruncode = !(SelectedServer.autoruncode)
if(href_list["network"])
var/newnet = input(usr, "Which network do you want to view?", "Comm Monitor", network) as null|text
if(newnet && canAccess(usr))
if(length(newnet) > 15)
temp = "<font color = #D70B00>- FAILED: NETWORK TAG STRING TOO LENGHTLY -</font color>"
else
network = newnet
screen = 0
servers = list()
temp = "<font color = #336699>- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -</font color>"
create_log("has set the network to [network].", usr)
updateUsrDialog()
return
/obj/machinery/computer/telecomms/traffic/attackby(var/obj/item/weapon/D as obj, var/mob/user as mob)
if(istype(D, /obj/item/weapon/screwdriver))
playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
if(do_after(user, 20))
if (src.stat & BROKEN)
user << "\blue The broken glass falls out."
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
new /obj/item/weapon/shard( src.loc )
var/obj/item/weapon/circuitboard/comm_traffic/M = new /obj/item/weapon/circuitboard/comm_traffic( A )
for (var/obj/C in src)
C.loc = src.loc
A.circuit = M
A.state = 3
A.icon_state = "3"
A.anchored = 1
del(src)
else
user << "\blue You disconnect the monitor."
var/obj/structure/computerframe/A = new /obj/structure/computerframe( src.loc )
var/obj/item/weapon/circuitboard/comm_traffic/M = new /obj/item/weapon/circuitboard/comm_traffic( A )
for (var/obj/C in src)
C.loc = src.loc
A.circuit = M
A.state = 4
A.icon_state = "4"
A.anchored = 1
del(src)
else if(istype(D, /obj/item/weapon/card/emag) && !emagged)
playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
emagged = 1
user << "\blue You you disable the security protocols"
src.updateUsrDialog()
return
/obj/machinery/computer/telecomms/traffic/proc/canAccess(var/mob/user)
if(issilicon(user) || in_range(user, src))
return 1
return 0

View File

@@ -69,11 +69,15 @@
// //
IfStatement IfStatement
var var
skip = 0
node node
BlockDefinition BlockDefinition
block block
else_block //may be null else_block //may be null
expression/cond expression/cond
statement/else_if
ElseIf
/* /*
Class: WhileLoop Class: WhileLoop

View File

@@ -60,6 +60,15 @@
New(name, token/t) New(name, token/t)
message="Function '[name]' defined twice." 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 Class: runtimeError
An error thrown by the interpreter in running the script. An error thrown by the interpreter in running the script.

View File

@@ -1,7 +1,7 @@
client/verb/tcssave() client/verb/tcssave()
set hidden = 1 set hidden = 1
if(mob.machine || issilicon(mob)) 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 var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob) if(Machine.editingcode != mob)
return return
@@ -9,9 +9,6 @@ client/verb/tcssave()
if(Machine.SelectedServer) if(Machine.SelectedServer)
var/obj/machinery/telecomms/server/Server = Machine.SelectedServer var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
var/tcscode=winget(src, "tcscode", "text") 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 Server.setcode( tcscode ) // this actually saves the code from input to the server
src << output(null, "tcserror") // clear the errors src << output(null, "tcserror") // clear the errors
else else
@@ -28,7 +25,7 @@ client/verb/tcssave()
client/verb/tcscompile() client/verb/tcscompile()
set hidden = 1 set hidden = 1
if(mob.machine || issilicon(mob)) 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 var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob) if(Machine.editingcode != mob)
return return
@@ -36,35 +33,40 @@ client/verb/tcscompile()
if(Machine.SelectedServer) if(Machine.SelectedServer)
var/obj/machinery/telecomms/server/Server = Machine.SelectedServer var/obj/machinery/telecomms/server/Server = Machine.SelectedServer
Server.setcode( winget(src, "tcscode", "text") ) // save code first Server.setcode( winget(src, "tcscode", "text") ) // save code first
var/list/compileerrors = Server.compile() // then compile the code!
// Output all the compile-time errors spawn(0)
src << output(null, "tcserror") // Output all the compile-time errors
src << output(null, "tcserror")
src << output("<font color = black>Please wait, compiling...</font>", "tcserror")
if(compileerrors.len) var/list/compileerrors = Server.compile(mob) // then compile the code!
src << output("<b>Compile Errors</b>", "tcserror") if(!telecomms_check(mob))
for(var/scriptError/e in compileerrors) return
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 if(compileerrors.len)
for(var/mob/M in Machine.viewingcode) src << output("<b>Compile Errors</b>", "tcserror")
if(M.client) for(var/scriptError/e in compileerrors)
M << output(null, "tcserror") src << output("<font color = red>\t>[e.message]</font color>", "tcserror")
M << output("<b>Compile Errors</b>", "tcserror") src << output("([compileerrors.len] errors)", "tcserror")
for(var/scriptError/e in compileerrors)
M << output("<font color = red>\t>[e.message]</font color>", "tcserror") // Output compile errors to all other people viewing the code too
M << output("([compileerrors.len] errors)", "tcserror") 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 else
src << output("<font color = blue>TCS compilation successful!</font color>", "tcserror") src << output("<font color = blue>TCS compilation successful!</font color>", "tcserror")
src << output("(0 errors)", "tcserror") src << output("(0 errors)", "tcserror")
for(var/mob/M in Machine.viewingcode) for(var/mob/M in Machine.viewingcode)
if(M.client) if(M.client)
M << output("<font color = blue>TCS compilation successful!</font color>", "tcserror") M << output("<font color = blue>TCS compilation successful!</font color>", "tcserror")
M << output("(0 errors)", "tcserror") M << output("(0 errors)", "tcserror")
else else
src << output(null, "tcserror") src << output(null, "tcserror")
@@ -79,56 +81,26 @@ client/verb/tcscompile()
client/verb/tcsrun() client/verb/tcsrun()
set hidden = 1 set hidden = 1
if(mob.machine || issilicon(mob)) 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 var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob) if(Machine.editingcode != mob)
return return
if(Machine.SelectedServer) if(Machine.SelectedServer)
var/obj/machinery/telecomms/server/Server = 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 else
// Finally, we run the code! signal.frequency = 1459
src << output("<font color = blue>TCS compilation successful! Code executed.</font color>", "tcserror") signal.data["name"] = ""
src << output("(0 errors)", "tcserror") signal.data["job"] = ""
signal.data["reject"] = 0
signal.data["server"] = Server
for(var/mob/M in Machine.viewingcode) Server.Compiler.Run(signal)
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)
else else
@@ -145,7 +117,7 @@ client/verb/tcsrun()
client/verb/exittcs() client/verb/exittcs()
set hidden = 1 set hidden = 1
if(mob.machine || issilicon(mob)) 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 var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode == mob) if(Machine.editingcode == mob)
Machine.storedcode = "[winget(mob, "tcscode", "text")]" Machine.storedcode = "[winget(mob, "tcscode", "text")]"
@@ -157,7 +129,7 @@ client/verb/exittcs()
client/verb/tcsrevert() client/verb/tcsrevert()
set hidden = 1 set hidden = 1
if(mob.machine || issilicon(mob)) 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 var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob) if(Machine.editingcode != mob)
return return
@@ -186,7 +158,7 @@ client/verb/tcsrevert()
client/verb/tcsclearmem() client/verb/tcsclearmem()
set hidden = 1 set hidden = 1
if(mob.machine || issilicon(mob)) 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 var/obj/machinery/computer/telecomms/traffic/Machine = mob.machine
if(Machine.editingcode != mob) if(Machine.editingcode != mob)
return return
@@ -208,4 +180,9 @@ client/verb/tcsclearmem()
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")
else else
src << output(null, "tcserror") 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

View File

@@ -10,11 +10,26 @@
HandleError(runtimeError/e) HandleError(runtimeError/e)
Compiler.Holder.add_entry(e.ToString(), "Execution Error") Compiler.Holder.add_entry(e.ToString(), "Execution Error")
GC()
..()
Compiler = null
/datum/TCS_Compiler /datum/TCS_Compiler
var/n_Interpreter/TCS_Interpreter/interpreter var/n_Interpreter/TCS_Interpreter/interpreter
var/obj/machinery/telecomms/server/Holder // the server that is running the code var/obj/machinery/telecomms/server/Holder // the server that is running the code
var/ready = 1 // 1 if ready to run 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 -- */ /* -- Compile a raw block of text -- */
proc/Compile(code as message) proc/Compile(code as message)
@@ -54,7 +69,9 @@
interpreter.SetVar("E" , 2.718281828) // value of e interpreter.SetVar("E" , 2.718281828) // value of e
interpreter.SetVar("SQURT2" , 1.414213562) // value of the square root of 2 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("false" , 0) // boolean shortcut to 0
interpreter.SetVar("TRUE" , 1) // boolean shortcut to 1 interpreter.SetVar("TRUE" , 1) // boolean shortcut to 1
interpreter.SetVar("true" , 1) // boolean shortcut to 1
interpreter.SetVar("NORTH" , NORTH) // NORTH (1) interpreter.SetVar("NORTH" , NORTH) // NORTH (1)
interpreter.SetVar("SOUTH" , SOUTH) // SOUTH (2) interpreter.SetVar("SOUTH" , SOUTH) // SOUTH (2)
@@ -93,13 +110,13 @@
interpreter.SetProc("broadcast", "tcombroadcast", signal, list("message", "freq", "source", "job")) interpreter.SetProc("broadcast", "tcombroadcast", signal, list("message", "freq", "source", "job"))
/* /*
-> Send a signal used by signallers. -> Send a code signal.
@format: signal(frequency, code) @format: signal(frequency, code)
@param frequency: Frequency to broadcast to @param frequency: Frequency to send the signal to
@param code: Code to send @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) -> 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("round", /proc/n_round)
interpreter.SetProc("clamp", /proc/n_clamp) interpreter.SetProc("clamp", /proc/n_clamp)
interpreter.SetProc("inrange", /proc/n_inrange) interpreter.SetProc("inrange", /proc/n_inrange)
interpreter.SetProc("rand", /proc/rand_chance)
// End of Donkie~ // End of Donkie~
// Time
interpreter.SetProc("time", /proc/time)
interpreter.SetProc("timestamp", /proc/timestamp)
// Run the compiled code // Run the compiled code
interpreter.Run() interpreter.Run()
@@ -193,21 +214,16 @@
// Backwards-apply variables onto signal data // Backwards-apply variables onto signal data
/* sanitize EVERYTHING. fucking players can't be trusted with SHIT */ /* sanitize EVERYTHING. fucking players can't be trusted with SHIT */
signal.data["message"] = interpreter.GetVar("$content") signal.data["message"] = interpreter.GetCleanVar("$content", signal.data["message"])
signal.frequency = interpreter.GetVar("$freq") signal.frequency = interpreter.GetCleanVar("$freq", signal.frequency)
var/setname = "" var/setname = interpreter.GetCleanVar("$source", signal.data["name"])
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>"
if(signal.data["name"] != setname) if(signal.data["name"] != setname)
signal.data["realname"] = setname signal.data["realname"] = setname
signal.data["name"] = setname signal.data["name"] = setname
signal.data["job"] = interpreter.GetVar("$job") signal.data["job"] = interpreter.GetCleanVar("$job", signal.data["job"])
signal.data["reject"] = !(interpreter.GetVar("$pass")) // set reject to the opposite of $pass signal.data["reject"] = !(interpreter.GetCleanVar("$pass")) // set reject to the opposite of $pass
// If the message is invalid, just don't broadcast it! // If the message is invalid, just don't broadcast it!
if(signal.data["message"] == "" || !signal.data["message"]) if(signal.data["message"] == "" || !signal.data["message"])
@@ -215,6 +231,8 @@
/* -- Actual language proc code -- */ /* -- Actual language proc code -- */
var/const/SIGNAL_COOLDOWN = 20 // 2 seconds
datum/signal datum/signal
proc/mem(var/address, var/value) proc/mem(var/address, var/value)
@@ -229,6 +247,37 @@ datum/signal
S.memory[address] = value 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) proc/tcombroadcast(var/message, var/freq, var/source, var/job)
var/datum/signal/newsign = new var/datum/signal/newsign = new
@@ -244,7 +293,7 @@ datum/signal
if(!source) if(!source)
source = "[html_encode(uppertext(S.id))]" source = "[html_encode(uppertext(S.id))]"
hradio = new // sets the hradio as a radio intercom hradio = new // sets the hradio as a radio intercom
if(!freq) if(!freq || (!isnum(freq) && text2num(freq) == null))
freq = 1459 freq = 1459
if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal if(findtext(num2text(freq), ".")) // if the frequency has been set as a decimal
freq *= 10 // shift the decimal one place freq *= 10 // shift the decimal one place
@@ -254,12 +303,9 @@ datum/signal
newsign.data["mob"] = null newsign.data["mob"] = null
newsign.data["mobtype"] = /mob/living/carbon/human newsign.data["mobtype"] = /mob/living/carbon/human
if(source in S.stored_names) newsign.data["name"] = source
newsign.data["name"] = source
else
newsign.data["name"] = "<i>[html_encode(uppertext(source))]<i>"
newsign.data["realname"] = newsign.data["name"] newsign.data["realname"] = newsign.data["name"]
newsign.data["job"] = job newsign.data["job"] = "[job]"
newsign.data["compression"] = 0 newsign.data["compression"] = 0
newsign.data["message"] = message newsign.data["message"] = message
newsign.data["type"] = 2 // artificial broadcast newsign.data["type"] = 2 // artificial broadcast
@@ -277,6 +323,8 @@ datum/signal
newsign.data["vmask"] = 0 newsign.data["vmask"] = 0
newsign.data["level"] = list() newsign.data["level"] = list()
newsign.sanitize_data()
var/pass = S.relay_information(newsign, "/obj/machinery/telecomms/hub") var/pass = S.relay_information(newsign, "/obj/machinery/telecomms/hub")
if(!pass) if(!pass)
S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters S.relay_information(newsign, "/obj/machinery/telecomms/broadcaster") // send this simple message to broadcasters

View File

@@ -94,6 +94,10 @@
/proc/delay(var/time) /proc/delay(var/time)
sleep(time) sleep(time)
// Clone of rand()
/proc/rand_chance(var/low = 0, var/high)
return rand(low, high)
// Clone of prob() // Clone of prob()
/proc/prob_chance(var/chance) /proc/prob_chance(var/chance)
return prob(chance) return prob(chance)
@@ -123,6 +127,7 @@
if(container) if(container)
if(istype(container, /list) || istext(container)) if(istype(container, /list) || istext(container))
return length(container) return length(container)
return 0
// BY DONKIE~ // BY DONKIE~
// String stuff // String stuff
@@ -134,6 +139,12 @@
if(istext(string)) if(istext(string))
return uppertext(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 //Makes a list where all indicies in a string is a seperate index in the list
// JUST A HELPER DON'T ADD TO NTSCRIPT // 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. 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) return text2list(string, separator)
proc/n_repeat(var/string, var/amount) proc/n_repeat(var/string, var/amount)
@@ -247,15 +260,18 @@ proc/n_inrange(var/num, var/min=-1, var/max=1)
// Non-recursive // Non-recursive
// Imported from Mono string.ReplaceUnchecked // Imported from Mono string.ReplaceUnchecked
/*
/proc/string_replacetext(var/haystack,var/a,var/b) /proc/string_replacetext(var/haystack,var/a,var/b)
if(istext(haystack)&&istext(a)&&istext(b)) if(istext(haystack)&&istext(a)&&istext(b))
var/i = 1 var/i = 1
var/lenh=lentext(haystack) var/lenh=lentext(haystack)
var/lena=lentext(a) var/lena=lentext(a)
//var/lenb=lentext(b)
var/count = 0 var/count = 0
var/list/dat = list() var/list/dat = list()
while (i < lenh) while (i < lenh)
var/found = findtext(haystack, a, i, 0) var/found = findtext(haystack, a, i, 0)
//diary << "findtext([haystack], [a], [i], 0)=[found]"
if (found == 0) // Not found if (found == 0) // Not found
break break
else else
@@ -263,50 +279,49 @@ proc/n_inrange(var/num, var/min=-1, var/max=1)
dat+=found dat+=found
count+=1 count+=1
else else
//diary << "Script found [a] [count] times, aborted"
break break
//diary << "Found [a] at [found]! Moving up..."
i = found + lena i = found + lena
if (count == 0) if (count == 0)
return haystack return haystack
//var/nlen = lenh + ((lenb - lena) * count)
var/buf = copytext(haystack,1,dat[1]) // Prefill var/buf = copytext(haystack,1,dat[1]) // Prefill
var/lastReadPos = 0 var/lastReadPos = 0
for (i = 1, i <= count, i++) for (i = 1, i <= count, i++)
var/precopy = dat[i] - lastReadPos-1 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) buf+=copytext(haystack,lastReadPos,precopy)
diary << "buf+=copytext([haystack],[lastReadPos],[precopy])"
diary<<"[buf]"
lastReadPos = dat[i] + lena lastReadPos = dat[i] + lena
//CharCopy (dest + curPos, replace, newValue.length);
buf+=b buf+=b
diary<<"[buf]"
buf+=copytext(haystack,lastReadPos, 0) buf+=copytext(haystack,lastReadPos, 0)
return buf return buf
*/
/proc/string_replacetext(text, find, replacement)
/proc/ntsl_send_signal(var/freq,var/code) if(istext(text) && istext(find) && istext(replacement))
if(isnum(freq)&&isnum(code)) var/find_len = length(find)
if(!radio_controller) if(find_len < 1) return text
sleep(20) . = ""
if(!radio_controller) 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 return
// Sanitize frequency
var/new_frequency = freq
if(new_frequency < 1200 || new_frequency > 1600)
new_frequency = sanitize_frequency(new_frequency)
// Sanitize code #undef SCRIPT_MAX_REPLACEMENTS_ALLOWED
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)

View File

@@ -21,6 +21,7 @@
ASSERT(program) ASSERT(program)
src.program = program src.program = program
CreateGlobalScope() CreateGlobalScope()
alertadmins = 0 // reset admin alerts
/* /*
Proc: Run Proc: Run
@@ -29,7 +30,6 @@
Run() Run()
cur_recursion = 0 // reset recursion cur_recursion = 0 // reset recursion
cur_statements = 0 // reset CPU tracking cur_statements = 0 // reset CPU tracking
alertadmins = 0
ASSERT(src.program) ASSERT(src.program)
RunBlock(src.program) RunBlock(src.program)
@@ -110,6 +110,17 @@
var/x = globalScope.variables[name] var/x = globalScope.variables[name]
return Eval(x) 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 Proc: CallProc
Calls a global function defined in the script and, amazingly enough, returns its return value. Remember to ensure that the function Calls a global function defined in the script and, amazingly enough, returns its return value. Remember to ensure that the function

View File

@@ -33,11 +33,11 @@
status=0 status=0
returnVal 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 cur_statements=0 // current amount of statements called
alertadmins=0 // set to 1 if the admins shouldn't be notified of anymore issues 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_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 cur_recursion=0 // current amount of recursion
/* /*
Var: persist Var: persist
@@ -55,6 +55,14 @@
if(program)Load(program) if(program)Load(program)
proc proc
/*
Set ourselves to Garbage Collect
*/
GC()
..()
container = null
/* /*
Proc: RaiseError Proc: RaiseError
Raises a runtime error. Raises a runtime error.
@@ -76,6 +84,19 @@
globalScope = S globalScope = S
return 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 Proc: RunBlock
Runs each statement in a block of code. Runs each statement in a block of code.
@@ -100,15 +121,7 @@
cur_statements++ cur_statements++
if(cur_statements >= max_statements) if(cur_statements >= max_statements)
RaiseError(new/runtimeError/MaxCPU()) RaiseError(new/runtimeError/MaxCPU())
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)
break break
if(istype(S, /node/statement/VariableAssignment)) if(istype(S, /node/statement/VariableAssignment))
@@ -171,6 +184,7 @@
// If recursion gets too high (max 50 nested functions) throw an error // If recursion gets too high (max 50 nested functions) throw an error
if(cur_recursion >= max_recursion) if(cur_recursion >= max_recursion)
AlertAdmins()
RaiseError(new/runtimeError/RecursionLimitReached()) RaiseError(new/runtimeError/RecursionLimitReached())
return 0 return 0
@@ -223,10 +237,21 @@
Checks a condition and runs either the if block or else block. Checks a condition and runs either the if block or else block.
*/ */
RunIf(node/statement/IfStatement/stmt) RunIf(node/statement/IfStatement/stmt)
if(Eval(stmt.cond)) if(!stmt.skip)
RunBlock(stmt.block) if(Eval(stmt.cond))
else if(stmt.else_block) RunBlock(stmt.block)
RunBlock(stmt.else_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 Proc: RunWhile
@@ -312,3 +337,4 @@
//TODO: check for invalid name //TODO: check for invalid name
S.variables["[name]"] = value S.variables["[name]"] = value

View File

@@ -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 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. 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, \ "while" = /n_Keyword/nS_Keyword/kwWhile, "break" = /n_Keyword/nS_Keyword/kwBreak, \
"continue" = /n_Keyword/nS_Keyword/kwContinue, \ "continue" = /n_Keyword/nS_Keyword/kwContinue, \
"return" = /n_Keyword/nS_Keyword/kwReturn, "def" = /n_Keyword/nS_Keyword/kwDef) "return" = /n_Keyword/nS_Keyword/kwReturn, "def" = /n_Keyword/nS_Keyword/kwDef)

View File

@@ -180,12 +180,18 @@
- <ParseParenExpression()> - <ParseParenExpression()>
- <ParseParamExpression()> - <ParseParamExpression()>
*/ */
ParseExpression(list/end=list(/token/end), list/ErrChars=list("{", "}")) ParseExpression(list/end=list(/token/end), list/ErrChars=list("{", "}"), check_functions = 0)
var/stack var/stack
opr=new opr=new
val=new val=new
src.expecting=VALUE src.expecting=VALUE
var/loop = 0
for() for()
loop++
if(loop > 800)
errors+=new/scriptError("Too many nested tokens.")
return
if(EndOfExpression(end)) if(EndOfExpression(end))
break break
if(istype(curToken, /token/symbol) && ErrChars.Find(curToken.value)) if(istype(curToken, /token/symbol) && ErrChars.Find(curToken.value))
@@ -206,6 +212,7 @@
NextToken() NextToken()
continue continue
val.Push(ParseParenExpression()) val.Push(ParseParenExpression())
else if(istype(curToken, /token/symbol)) //Operator found. 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. var/node/expression/operator/curOperator //Figure out whether it is unary or binary and get a new instance.
if(src.expecting==OPERATOR) if(src.expecting==OPERATOR)
@@ -226,16 +233,24 @@
continue continue
opr.Push(curOperator) opr.Push(curOperator)
src.expecting=VALUE src.expecting=VALUE
else if(ntok && ntok.value=="(" && istype(ntok, /token/symbol)\ else if(ntok && ntok.value=="(" && istype(ntok, /token/symbol)\
&& istype(curToken, /token/word)) //Parse function call && istype(curToken, /token/word)) //Parse function call
var/token/preToken=curToken
var/old_expect=src.expecting if(!check_functions)
var/fex=ParseFunctionExpression()
if(old_expect!=VALUE) var/token/preToken=curToken
errors+=new/scriptError/ExpectedToken("operator", preToken) var/old_expect=src.expecting
NextToken() var/fex=ParseFunctionExpression()
continue if(old_expect!=VALUE)
val.Push(fex) 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 else if(istype(curToken, /token/keyword)) //inline keywords
var/n_Keyword/kw=options.keywords[curToken.value] var/n_Keyword/kw=options.keywords[curToken.value]
kw=new kw(inline=1) kw=new kw(inline=1)
@@ -244,6 +259,7 @@
return return
else else
errors+=new/scriptError/BadToken(curToken) errors+=new/scriptError/BadToken(curToken)
else if(istype(curToken, /token/end)) //semicolon found where it wasn't expected else if(istype(curToken, /token/end)) //semicolon found where it wasn't expected
errors+=new/scriptError/BadToken(curToken) errors+=new/scriptError/BadToken(curToken)
NextToken() NextToken()
@@ -255,6 +271,7 @@
continue continue
val.Push(GetExpression(curToken)) val.Push(GetExpression(curToken))
src.expecting=OPERATOR src.expecting=OPERATOR
NextToken() NextToken()
while(opr.Top()) Reduce(opr, val) //Reduce the value stack completely while(opr.Top()) Reduce(opr, val) //Reduce the value stack completely
@@ -280,12 +297,16 @@
for() for()
loops++ loops++
if(loops>=1000) if(loops>=800)
CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;") errors += new/scriptError("Too many nested expressions.")
break
//CRASH("Something TERRIBLE has gone wrong in ParseFunctionExpression ;__;")
if(istype(curToken, /token/symbol) && curToken.value==")") if(istype(curToken, /token/symbol) && curToken.value==")")
return exp return exp
exp.parameters+=ParseParamExpression() exp.parameters+=ParseParamExpression()
if(errors.len)
return exp
if(curToken.value==","&&istype(curToken, /token/symbol))NextToken() //skip comma if(curToken.value==","&&istype(curToken, /token/symbol))NextToken() //skip comma
if(istype(curToken, /token/end)) //Prevents infinite loop... if(istype(curToken, /token/end)) //Prevents infinite loop...
errors+=new/scriptError/ExpectedToken(")") errors+=new/scriptError/ExpectedToken(")")
@@ -310,5 +331,6 @@
See Also: See Also:
- <ParseExpression()> - <ParseExpression()>
*/ */
ParseParamExpression() ParseParamExpression(var/check_functions = 0)
return ParseExpression(list(",", ")")) var/cf = check_functions
return ParseExpression(list(",", ")"), check_functions = cf)

View File

@@ -51,9 +51,9 @@ var/const/Represents a special statement in the code triggered by a keyword.
kwReturn kwReturn
Parse(n_Parser/nS_Parser/parser) Parse(n_Parser/nS_Parser/parser)
.=KW_PASS .=KW_PASS
if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock)) if(istype(parser.curBlock, /node/BlockDefinition/GlobalBlock)) // Exit out of the program by setting the tokens list size to the same as index.
parser.errors+=new/scriptError/BadReturn(parser.curToken) parser.tokens.len = parser.index
. = KW_WARN return
var/node/statement/ReturnStatement/stmt=new var/node/statement/ReturnStatement/stmt=new
parser.NextToken() //skip 'return' token parser.NextToken() //skip 'return' token
stmt.value=parser.ParseExpression() stmt.value=parser.ParseExpression()
@@ -73,6 +73,31 @@ var/const/Represents a special statement in the code triggered by a keyword.
stmt.block=new stmt.block=new
parser.AddBlock(stmt.block) 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 kwElse
Parse(n_Parser/nS_Parser/parser) Parse(n_Parser/nS_Parser/parser)
.=KW_PASS .=KW_PASS

View File

@@ -174,8 +174,9 @@
var/loops = 0 var/loops = 0
for() for()
loops++ loops++
if(loops>=6000) if(loops>=800)
CRASH("Something TERRIBLE has gone wrong in ParseFunctionStatement ;__;") errors +=new/scriptError("Cannot find ending params.")
return
if(!curToken) if(!curToken)
errors+=new/scriptError/EndOfFile() errors+=new/scriptError/EndOfFile()
@@ -184,6 +185,6 @@
curBlock.statements+=stmt curBlock.statements+=stmt
NextToken() //Skip close parenthesis NextToken() //Skip close parenthesis
return return
var/node/expression/P=ParseParamExpression() var/node/expression/P=ParseParamExpression(check_functions = 1)
stmt.parameters+=P stmt.parameters+=P
if(istype(curToken, /token/symbol) && curToken.value==",") NextToken() if(istype(curToken, /token/symbol) && curToken.value==",") NextToken()