diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index 7fc6064fcb..9e14ec3b5d 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -265,6 +265,9 @@ var if (R.client && R.client.STFU_radio) //Adminning with 80 people on can be fun when you're trying to talk and all you can hear is radios. continue + if(istype(M, /mob/new_player)) // we don't want new players to hear messages. rare but generates runtimes. + continue + // --- Check for compression --- if(compression > 0) diff --git a/code/game/machinery/telecomms/logbrowser.dm b/code/game/machinery/telecomms/logbrowser.dm index eb6d6368bb..c64b7bae7e 100644 --- a/code/game/machinery/telecomms/logbrowser.dm +++ b/code/game/machinery/telecomms/logbrowser.dm @@ -58,10 +58,12 @@ for(var/datum/comm_log_entry/C in SelectedServer.log_entries) i++ - dat += "
  • [C.name] \[X\]
    " // If the log is a speech file if(C.input_type == "Speech File") + + dat += "
  • [C.name] \[X\]
    " + // -- Determine race of orator -- var/race // The actual race of the mob @@ -113,9 +115,10 @@ dat += "

  • " else if(C.input_type == "Execution Error") - dat += "Data type: [C.input_type]
    " - dat += "Source: Internal server code
    " - dat += "Contents: \"[C.parameters["message"]]\"
    " + + dat += "
  • [C.name] \[X\]
    " + dat += "Output: \"[C.parameters["message"]]\"
    " + dat += "

  • " dat += "" diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index 05780256fe..6f1e45765a 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -403,7 +403,9 @@ log.parameters["message"] = signal.data["message"] log.parameters["name"] = signal.data["name"] log.parameters["realname"] = signal.data["realname"] - log.parameters["uspeech"] = M.universal_speak + + if(!istype(M, /mob/new_player)) + log.parameters["uspeech"] = M.universal_speak // If the signal is still compressed, make the log entry gibberish if(signal.data["compression"] > 0) @@ -453,9 +455,12 @@ proc/add_entry(var/content, var/input) var/datum/comm_log_entry/log = new var/identifier = num2text( rand(-1000,1000) + world.time ) - log.name = "data packet ([md5(identifier)])" + log.name = "[input] ([md5(identifier)])" log.input_type = input log.parameters["message"] = content + log_entries.Add(log) + update_logs() + diff --git a/code/game/machinery/telecomms/traffic_control.dm b/code/game/machinery/telecomms/traffic_control.dm index bb495decc3..753acf6e79 100644 --- a/code/game/machinery/telecomms/traffic_control.dm +++ b/code/game/machinery/telecomms/traffic_control.dm @@ -10,6 +10,7 @@ screen = 0 // the screen number: list/servers = list() // the servers located by the computer mob/editingcode + mob/lasteditor list/viewingcode = list() obj/machinery/telecomms/server/SelectedServer @@ -49,13 +50,12 @@ showcode = dd_replacetext(storedcode, "\"", "\\\"") for(var/mob/M in viewingcode) - if(M.machine == src && M in view(1, src)) + if( (M.machine == src && M in view(1, src) ) || issilicon(M)) winset(M, "tcscode", "is-disabled=true") winset(M, "tcscode", "text=\"[showcode]\"") else - if(!issilicon(M)) - viewingcode.Remove(M) - winshow(M, "Telecomms IDE", 0) // hide the window! + viewingcode.Remove(M) + winshow(M, "Telecomms IDE", 0) // hide the window! sleep(5) @@ -164,6 +164,7 @@ 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") diff --git a/code/modules/detectivework/detective_work.dm b/code/modules/detectivework/detective_work.dm index d298d6d505..dce59c2d0f 100644 --- a/code/modules/detectivework/detective_work.dm +++ b/code/modules/detectivework/detective_work.dm @@ -710,63 +710,89 @@ obj/machinery/computer/forensic_scanning obj/item/clothing/shoes/var track_blood = 0 mob/living/carbon/human/track_blood_mob + track_blood_type mob/var bloody_hands = 0 mob/living/carbon/human/bloody_hands_mob track_blood mob/living/carbon/human/track_blood_mob + track_blood_type obj/item/clothing/gloves/var transfer_blood = 0 mob/living/carbon/human/bloody_hands_mob -obj/effect/decal/cleanable/blood/var - track_amt = 2 +obj/effect/decal/cleanable/var + track_amt = 3 mob/blood_owner turf/Exited(mob/living/carbon/human/M) if(istype(M,/mob/living) && !istype(M,/mob/living/carbon/metroid)) - if(!istype(src, /turf/space)) // Bloody tracks code starts here - if(M.track_blood > 0) - M.track_blood-- - src.add_bloody_footprints(M.track_blood_mob,1,M.dir,get_tracks(M)) - else if(istype(M,/mob/living/carbon/human)) - if(M.shoes) - if(M.shoes.track_blood > 0) - M.shoes.track_blood-- - src.add_bloody_footprints(M.shoes.track_blood_mob,1,M.dir,M.shoes.name) // And bloody tracks end here + var/dofoot = 1 + if(istype(M,/mob/living/simple_animal)) + if(!(istype(M,/mob/living/simple_animal/cat) || istype(M,/mob/living/simple_animal/corgi) || istype(M,/mob/living/simple_animal/constructwraith))) + dofoot = 0 + + if(dofoot) + + if(!istype(src, /turf/space)) // Bloody tracks code starts here + if(M.track_blood > 0) + M.track_blood-- + src.add_bloody_footprints(M.track_blood_mob,1,M.dir,get_tracks(M),M.track_blood_type) + else if(istype(M,/mob/living/carbon/human)) + if(M.shoes) + if(M.shoes.track_blood > 0) + M.shoes.track_blood-- + src.add_bloody_footprints(M.shoes.track_blood_mob,1,M.dir,M.shoes.name,M.shoes.track_blood_type) // And bloody tracks end here . = ..() turf/Entered(mob/living/carbon/human/M) if(istype(M,/mob/living) && !istype(M,/mob/living/carbon/metroid)) - if(M.track_blood > 0) - M.track_blood-- - src.add_bloody_footprints(M.track_blood_mob,0,M.dir,get_tracks(M)) - else if(istype(M,/mob/living/carbon/human)) - if(M.shoes && !istype(src,/turf/space)) - if(M.shoes.track_blood > 0) - M.shoes.track_blood-- - src.add_bloody_footprints(M.shoes.track_blood_mob,0,M.dir,M.shoes.name) + var/dofoot = 1 + if(istype(M,/mob/living/simple_animal)) + if(!(istype(M,/mob/living/simple_animal/cat) || istype(M,/mob/living/simple_animal/corgi) || istype(M,/mob/living/simple_animal/constructwraith))) + dofoot = 0 + + if(dofoot) + + if(M.track_blood > 0) + M.track_blood-- + src.add_bloody_footprints(M.track_blood_mob,0,M.dir,get_tracks(M),M.track_blood_type) + else if(istype(M,/mob/living/carbon/human)) + if(M.shoes && !istype(src,/turf/space)) + if(M.shoes.track_blood > 0) + M.shoes.track_blood-- + src.add_bloody_footprints(M.shoes.track_blood_mob,0,M.dir,M.shoes.name,M.shoes.track_blood_type) - for(var/obj/effect/decal/cleanable/blood/B in src) - if(B.track_amt <= 0) continue - if(B.type != /obj/effect/decal/cleanable/blood/tracks) - if(istype(M,/mob/living/carbon/human)) - if(M.shoes) - M.shoes.add_blood(B.blood_owner) - M.shoes.track_blood_mob = B.blood_owner - M.shoes.track_blood = max(M.shoes.track_blood,8) - else - M.add_blood(B.blood_owner) - M.track_blood_mob = B.blood_owner - M.track_blood = max(M.track_blood,rand(4,8)) - B.track_amt-- - break + for(var/obj/effect/decal/cleanable/B in src) + if(B:track_amt <= 0) continue + if(B.type != /obj/effect/decal/cleanable/blood/tracks) + if(istype(B, /obj/effect/decal/cleanable/xenoblood) || istype(B, /obj/effect/decal/cleanable/blood) || istype(B, /obj/effect/decal/cleanable/oil) || istype(B, /obj/effect/decal/cleanable/robot_debris)) + + var/track_type = "blood" + if(istype(B, /obj/effect/decal/cleanable/xenoblood)) + track_type = "xeno" + else if(istype(B, /obj/effect/decal/cleanable/oil) || istype(B, /obj/effect/decal/cleanable/robot_debris)) + track_type = "oil" + + if(istype(M,/mob/living/carbon/human)) + if(M.shoes) + M.shoes.add_blood(B.blood_owner) + M.shoes.track_blood_mob = B.blood_owner + M.shoes.track_blood = max(M.shoes.track_blood,8) + M.shoes.track_blood_type = track_type + else + M.add_blood(B.blood_owner) + M.track_blood_mob = B.blood_owner + M.track_blood = max(M.track_blood,rand(4,8)) + M.track_blood_type = track_type + B.track_amt-- + break . = ..() -turf/proc/add_bloody_footprints(mob/living/carbon/human/M,leaving,d,info) +turf/proc/add_bloody_footprints(mob/living/carbon/human/M,leaving,d,info,bloodcolor) for(var/obj/effect/decal/cleanable/blood/tracks/T in src) - if(T.dir == d) + if(T.dir == d && findtext(T.icon, bloodcolor)) if((leaving && T.icon_state == "steps2") || (!leaving && T.icon_state == "steps1")) T.desc = "These bloody footprints appear to have been made by [info]." if(T.blood_DNA) @@ -780,12 +806,29 @@ turf/proc/add_bloody_footprints(mob/living/carbon/human/M,leaving,d,info) return var/obj/effect/decal/cleanable/blood/tracks/this = new(src) this.icon = 'footprints.dmi' + + var/preiconstate = "" + + if(info == "animal paws") + preiconstate = "paw" + else if(info == "alien claws") + preiconstate = "claw" + else if(info == "small alien feet") + preiconstate = "paw" + if(leaving) - this.icon_state = "blood2" + this.icon_state = "[bloodcolor][preiconstate]2" else - this.icon_state = "blood1" + this.icon_state = "[bloodcolor][preiconstate]1" this.dir = d - this.desc = "These bloody footprints appear to have been made by [info]." + + if(bloodcolor == "blood") + this.desc = "These bloody footprints appear to have been made by [info]." + else if(bloodcolor == "xeno") + this.desc = "These acidic bloody footprints appear to have been made by [info]." + else if(bloodcolor == "oil") + this.desc = "These oil footprints appear to have been made by [info]." + if(istype(M,/mob/living/carbon/human)) if(this.blood_DNA.len) this.blood_DNA.len++ @@ -797,12 +840,14 @@ proc/get_tracks(mob/M) if(istype(M,/mob/living)) if(istype(M,/mob/living/carbon/human)) . = "human feet" - else if(istype(M,/mob/living/carbon/monkey)) - . = "monkey paws" + else if(istype(M,/mob/living/carbon/monkey) || istype(M,/mob/living/simple_animal/cat) || istype(M,/mob/living/simple_animal/corgi) || istype(M,/mob/living/simple_animal/crab)) + . = "animal paws" else if(istype(M,/mob/living/silicon/robot)) . = "robot feet" - else if(istype(M,/mob/living/carbon/alien)) + else if(istype(M,/mob/living/carbon/alien/humanoid)) . = "alien claws" + else if(istype(M,/mob/living/carbon/alien/larva)) + . = "small alien feet" else . = "an unknown creature" diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 74d9a0d6b6..b17f608c46 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -728,7 +728,8 @@ lying = 1 stat = 0 if (paralysis > 0) - handle_dreams() + if(sleeping > 0) + handle_dreams() AdjustParalysis(-1) blinded = 1 lying = 1 diff --git a/code/modules/scripting/Errors.dm b/code/modules/scripting/Errors.dm index 361d762048..bb92bf8f40 100644 --- a/code/modules/scripting/Errors.dm +++ b/code/modules/scripting/Errors.dm @@ -121,4 +121,8 @@ DivisionByZero name="DivideByZeroError" - message="Division by zero attempted." \ No newline at end of file + message="Division by zero attempted." + + MaxCPU + name="MaxComputationalUse" + message="Maximum amount of computational cycles reached (>= 1000)." \ No newline at end of file diff --git a/code/modules/scripting/Implementations/Telecomms.dm b/code/modules/scripting/Implementations/Telecomms.dm index 6ab82d9d0f..5801cf2eb4 100644 --- a/code/modules/scripting/Implementations/Telecomms.dm +++ b/code/modules/scripting/Implementations/Telecomms.dm @@ -47,6 +47,8 @@ if(!interpreter) return + interpreter.container = src + interpreter.SetVar("PI" , 3.141592653) // value of pi interpreter.SetVar("E" , 2.718281828) // value of e interpreter.SetVar("SQURT2" , 1.414213562) // value of the square root of 2 @@ -137,7 +139,7 @@ @param container: the list or container to measure */ - interpreter.SetProc("length", /proc/smartfind) + interpreter.SetProc("length", /proc/smartlength) /* -- Clone functions, carried from default BYOND procs --- */ diff --git a/code/modules/scripting/Interpreter/Interaction.dm b/code/modules/scripting/Interpreter/Interaction.dm index 0dda1656d6..f0b7c988d2 100644 --- a/code/modules/scripting/Interpreter/Interaction.dm +++ b/code/modules/scripting/Interpreter/Interaction.dm @@ -28,6 +28,8 @@ */ Run() cur_recursion = 0 // reset recursion + cur_statements = 0 // reset CPU tracking + alertadmins = 0 ASSERT(src.program) RunBlock(src.program) diff --git a/code/modules/scripting/Interpreter/Interpreter.dm b/code/modules/scripting/Interpreter/Interpreter.dm index e9860176f3..70849ffa15 100644 --- a/code/modules/scripting/Interpreter/Interpreter.dm +++ b/code/modules/scripting/Interpreter/Interpreter.dm @@ -24,6 +24,8 @@ stack scopes = new() functions = new() + + datum/container // associated container for interpeter /* Var: status A variable indicating that the rest of the current block should be skipped. This may be set to any combination of . @@ -31,10 +33,12 @@ status=0 returnVal - max_iterations=100 // max iterations without any kind of delay - cur_iterations=0 // current iteration - max_recursion=50 // max recursions without returning anything (or completing the code block) - cur_recursion=0 // current amount of recursion + max_statements=1000 // 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) + cur_recursion=0 // current amount of recursion /* Var: persist If 0, global variables will be reset after Run() finishes. @@ -88,56 +92,74 @@ CreateGlobalScope() curScope = globalScope - for(var/node/statement/S in Block.statements) - while(paused) sleep(10) - if(istype(S, /node/statement/VariableAssignment)) - var/node/statement/VariableAssignment/stmt = S - var/name = stmt.var_name.id_name - if(!stmt.object) - // Below we assign the variable first to null if it doesn't already exist. - // This is necessary for assignments like +=, and when the variable is used in a function - // If the variable already exists in a different block, then AssignVariable will automatically use that one. - if(!IsVariableAccessible(name)) - AssignVariable(name, null) - AssignVariable(name, Eval(stmt.value)) + if(cur_statements < max_statements) + + for(var/node/statement/S in Block.statements) + while(paused) sleep(10) + + 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) + break + + if(istype(S, /node/statement/VariableAssignment)) + var/node/statement/VariableAssignment/stmt = S + var/name = stmt.var_name.id_name + if(!stmt.object) + // Below we assign the variable first to null if it doesn't already exist. + // This is necessary for assignments like +=, and when the variable is used in a function + // If the variable already exists in a different block, then AssignVariable will automatically use that one. + if(!IsVariableAccessible(name)) + AssignVariable(name, null) + AssignVariable(name, Eval(stmt.value)) + else + var/datum/D = Eval(GetVariable(stmt.object.id_name)) + if(!D) return + D.vars[stmt.var_name.id_name] = Eval(stmt.value) + else if(istype(S, /node/statement/VariableDeclaration)) + //VariableDeclaration nodes are used to forcibly declare a local variable so that one in a higher scope isn't used by default. + var/node/statement/VariableDeclaration/dec=S + if(!dec.object) + AssignVariable(dec.var_name.id_name, null, curScope) + else + var/datum/D = Eval(GetVariable(dec.object.id_name)) + if(!D) return + D.vars[dec.var_name.id_name] = null + else if(istype(S, /node/statement/FunctionCall)) + RunFunction(S) + else if(istype(S, /node/statement/FunctionDefinition)) + //do nothing + else if(istype(S, /node/statement/WhileLoop)) + RunWhile(S) + else if(istype(S, /node/statement/IfStatement)) + RunIf(S) + else if(istype(S, /node/statement/ReturnStatement)) + if(!curFunction) + RaiseError(new/runtimeError/UnexpectedReturn()) + continue + status |= RETURNING + returnVal=Eval(S:value) + break + else if(istype(S, /node/statement/BreakStatement)) + status |= BREAKING + break + else if(istype(S, /node/statement/ContinueStatement)) + status |= CONTINUING + break else - var/datum/D = Eval(GetVariable(stmt.object.id_name)) - if(!D) return - D.vars[stmt.var_name.id_name] = Eval(stmt.value) - else if(istype(S, /node/statement/VariableDeclaration)) - //VariableDeclaration nodes are used to forcibly declare a local variable so that one in a higher scope isn't used by default. - var/node/statement/VariableDeclaration/dec=S - if(!dec.object) - AssignVariable(dec.var_name.id_name, null, curScope) - else - var/datum/D = Eval(GetVariable(dec.object.id_name)) - if(!D) return - D.vars[dec.var_name.id_name] = null - else if(istype(S, /node/statement/FunctionCall)) - RunFunction(S) - else if(istype(S, /node/statement/FunctionDefinition)) - //do nothing - else if(istype(S, /node/statement/WhileLoop)) - RunWhile(S) - else if(istype(S, /node/statement/IfStatement)) - RunIf(S) - else if(istype(S, /node/statement/ReturnStatement)) - if(!curFunction) - RaiseError(new/runtimeError/UnexpectedReturn()) - continue - status |= RETURNING - returnVal=Eval(S:value) - break - else if(istype(S, /node/statement/BreakStatement)) - status |= BREAKING - break - else if(istype(S, /node/statement/ContinueStatement)) - status |= CONTINUING - break - else - RaiseError(new/runtimeError/UnknownInstruction()) - if(status) - break + RaiseError(new/runtimeError/UnknownInstruction()) + if(status) + break + curScope = scopes.Pop() /* @@ -212,14 +234,9 @@ */ RunWhile(node/statement/WhileLoop/stmt) var/i=1 - if(!cur_iterations) - cur_iterations = 1 while(Eval(stmt.cond) && Iterate(stmt.block, i++)) - cur_iterations++ continue status &= ~BREAKING - cur_iterations -= i - if(cur_iterations <= 0) cur_iterations = 0 /* Proc:Iterate @@ -227,7 +244,7 @@ */ Iterate(node/BlockDefinition/block, count) RunBlock(block) - if(max_iterations > 0 && (count >= max_iterations || cur_iterations + 1 >= max_iterations)) + if(max_iterations > 0 && count >= max_iterations) RaiseError(new/runtimeError/IterationLimitReached()) return 0 if(status & (BREAKING|RETURNING)) diff --git a/icons/effects/footprints.dmi b/icons/effects/footprints.dmi index 091b6398c4..ede9c8e17d 100644 Binary files a/icons/effects/footprints.dmi and b/icons/effects/footprints.dmi differ