Adds OpenDream linting (#21922)

* Adds OD linting

* lint errors

* more fixes
This commit is contained in:
John Willard
2024-05-08 03:04:00 +00:00
committed by GitHub
parent 631fe86af9
commit ca1ed861c7
33 changed files with 145 additions and 99 deletions

View File

@@ -57,6 +57,25 @@ jobs:
with:
outputFile: output-annotations.txt
odlint:
if: ( !contains(github.event.head_commit.message, '[ci skip]') )
name: "Lint with OpenDream"
runs-on: ubuntu-22.04
concurrency:
group: odlint-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v4
- uses: robinraju/release-downloader@v1.9
with:
repository: "OpenDreamProject/OpenDream"
tag: "latest"
fileName: "DMCompiler_linux-x64.tar.gz"
extract: true
- name: Run OpenDream
run: |
./DMCompiler_linux-x64/DMCompiler yogstation.dme --suppress-unimplemented --define=CIBUILDING
compile:
name: Compile All Maps
runs-on: ubuntu-20.04
@@ -165,7 +184,7 @@ jobs:
- name: Compile
run: |
tools/build/build --ci dm -DCIBUILDING
tools/travis/dm.sh -DTRAVISBUILDING -DANSICOLORS yogstation.dme || travis_terminate 1
tools/travis/dm.sh -DCIBUILDING -DANSICOLORS yogstation.dme || travis_terminate 1
- name: Prepare Artifacts
run: |

3
.gitignore vendored
View File

@@ -222,3 +222,6 @@ tools/MapAtmosFixer/MapAtmosFixer/bin/*
# ezdb
/db/
/config/ezdb.txt
# Running OpenDream locally
tgstation.json

10
__odlint.dm Normal file
View File

@@ -0,0 +1,10 @@
// This file is included right at the start of the DME.
// Its purpose is to enable multiple lints (pragmas) that are supported by OpenDream to better validate the codebase
// These are essentially nitpicks the DM compiler should pick up on but doesnt
#if !defined(SPACEMAN_DMM) && defined(OPENDREAM)
// This is in a separate file as a hack to avoid SpacemanDMM
// evaluating the #pragma lines, even if its outside a block it cares about
// (Also so people can code-own it. Shoutout to AA)
#include "tools/ci/od_lints.dm"
#endif

View File

@@ -1,2 +1,6 @@
/// Takes a datum as input, returns its ref string
#define text_ref(datum) ref(datum)
/// A null statement to guard against EmptyBlock lint without necessitating the use of pass()
/// Used to avoid proc-call overhead. But use sparingly. Probably pointless in most places.
#define EMPTY_BLOCK_GUARD ;

View File

@@ -31,7 +31,6 @@
//Blob
/// blob gets a free reroll every X time
#define BLOB_REROLL_TIME 2400
#define BLOB_SPREAD_COST 4
#define OVERMIND_STARTING_AUTO_PLACE_TIME 6 MINUTES
/// blob refunds this much if it attacks and doesn't spread
#define BLOB_ATTACK_REFUND 2

View File

@@ -15,12 +15,12 @@
// below, their accesses are optimized away.
/// A text string of the verb's name.
var/name as text
var/name = null as text|null
/// The verb's help text or description.
var/desc as text
var/desc = null as text|null
/// The category or tab the verb will appear in.
var/category as text
var/category = null as text|null
/// Only clients/mobs with `see_invisibility` higher can use the verb.
var/invisibility as num
var/invisibility = null as num|null
/// Whether or not the verb appears in statpanel and commandbar when you press space
var/hidden as num
var/hidden = null as num|null

View File

@@ -94,8 +94,7 @@
return "northwest"
if(10)
return "southwest"
else
return
return NONE
//Turns text into proper directions
/proc/text2dir(direction)
@@ -116,8 +115,7 @@
return 6
if("SOUTHWEST")
return 10
else
return
return NONE
//Converts an angle (degrees) into an ss13 direction
/proc/angle2dir(degree)

View File

@@ -12,20 +12,9 @@
//If you update these values, update the message in the #error
#define MAX_BYOND_MAJOR 515
#define MAX_BYOND_MINOR 1640
// You can define IGNORE_MAX_BYOND_VERSION to bypass the max version check.
// Note: This will likely break the game, especially any extools/auxtools linkage. Only use if you know what you're doing!
#ifdef OPENDREAM // Thanks, Altoids!
#define IGNORE_MAX_BYOND_VERSION
#endif
#ifdef SPACEMAN_DMM // dm-langserver is now on 515 and we aren't
#define IGNORE_MAX_BYOND_VERSION
#endif
#if ((DM_VERSION > MAX_BYOND_MAJOR) || (DM_BUILD > MAX_BYOND_MINOR)) && !defined(IGNORE_MAX_BYOND_VERSION)
// Not updating until we fully move to 515
#error Your version of BYOND is too new to compile this project. Download version 515.1640 at www.byond.com/download/build/515/515.1640_byond.exe
#if ((DM_VERSION > MAX_BYOND_MAJOR) || (DM_BUILD > MAX_BYOND_MINOR)) && !defined(SPACEMAN_DMM)
#error Your version of BYOND is too new to compile this project.
#error Download version 515.1640 at www.byond.com/download/build/515/515.1640_byond.exe
#endif
// 515 split call for external libraries into call_ext

View File

@@ -48,7 +48,7 @@
#define FIND_REF_NO_CHECK_TICK
#endif
#ifdef TRAVISBUILDING
#if defined(CIBUILDING) && !defined(OPENDREAM)
#define UNIT_TESTS
#endif
@@ -63,7 +63,7 @@
#define EXTOOLS (world.system_type == MS_WINDOWS ? "byond-extools.dll" : "libbyond-extools.so")
#ifdef TRAVISBUILDING
#ifdef CIBUILDING
// Turdis is special :)
#define CBT
#endif
@@ -73,10 +73,17 @@
#define CBT
#endif
#if !defined(CBT) && !defined(SPACEMAN_DMM)
#warn Building with Dream Maker is no longer supported and may result in errors.
#warn In order to build, run BUILD.bat in the root directory.
#warn Consider switching to VSCode editor instead, where you can press Ctrl+Shift+B to build.
#if defined(OPENDREAM)
#if !defined(CIBUILDING)
#warn You are building with OpenDream. Remember to build TGUI manually.
#warn You can do this by running tgui-build.cmd from the bin directory.
#endif
#else
#if !defined(CBT) && !defined(SPACEMAN_DMM)
#warn Building with Dream Maker is no longer supported and will result in errors.
#warn In order to build, run BUILD.cmd in the root directory.
#warn Consider switching to VSCode editor instead, where you can press Ctrl+Shift+B to build.
#endif
#endif
// A reasonable number of maximum overlays an object needs

View File

@@ -31,8 +31,7 @@ SUBSYSTEM_DEF(acid)
return
continue
if(O.acid_level && O.acid_processing())
else
if(!O.acid_level || !O.acid_processing())
O.cut_overlay(GLOB.acid_overlay, TRUE)
processing -= O

View File

@@ -198,7 +198,7 @@
addtimer(VARSET_CALLBACK(src, cloud_id, cloud_id), NANITE_SYNC_DELAY, TIMER_UNIQUE)//return it to normal, intentionally missing the next sync timer
cloud_id = 0 //temporarily disable resyncing so rogue programs actually have a chance to do something
/datum/component/nanites/proc/on_shock(datum/source, shock_damage, obj/source, siemens_coeff, zone, tesla_shock)
/datum/component/nanites/proc/on_shock(datum/source, shock_damage, obj/source_object, siemens_coeff, zone, tesla_shock)
nanite_volume *= 1 - (rand(0.10, 0.35)**siemens_coeff) //Lose 10-35% of nanites
adjust_nanites(null, -(rand(5, 50)**siemens_coeff)) //Lose 5-50 flat nanite volume
for(var/X in programs)

View File

@@ -56,9 +56,8 @@
if(5 to 6)
//a dolphino
weighted_feature_spawn_list += pick(/mob/living/simple_animal/hostile/retaliate/dolphin, /mob/living/simple_animal/hostile/retaliate/dolphin/manatee)
else
//you get nothing good day sir
if(7 to 10)
return
/datum/dungeon_room_theme/maintenance/space/hostile
room_type = ROOM_RATING_HOSTILE

View File

@@ -179,7 +179,6 @@
//but travelling east and turning south gives you another south to east bend
//if(get_dir(trail_step, trail_step_next) == NORTH || get_dir(trail_step, trail_step_next) == SOUTH)
flip = TRUE
else
new effect_type(oldposition, trail_dir, sniffer, flip, trail_color)
oldposition = get_turf(holder)

View File

@@ -80,6 +80,7 @@
switch(infestation)
if(0 to WOUND_INFECTION_MODERATE)
return
if(WOUND_INFECTION_MODERATE to WOUND_INFECTION_SEVERE)
if(prob(30))
victim.adjustToxLoss(0.2)

View File

@@ -24,7 +24,7 @@
switch(result)
if (INITIALIZE_HINT_NORMAL)
// pass
EMPTY_BLOCK_GUARD // Pass
if(INITIALIZE_HINT_LATELOAD)
if(arguments[1]) //mapload
late_loaders += A

View File

@@ -64,11 +64,8 @@
CRASH("/atom/proc/run_atom_armor was called on [src] without being implemented as a type that uses integrity!")
if(damage_flag == MELEE && damage_amount < damage_deflection)
return 0
switch(damage_type)
if(BRUTE)
if(BURN)
else
return 0
if(damage_type != BRUTE && damage_type != BURN)
return 0
var/armor_protection = 0
if(damage_flag)
armor_protection = armor.getRating(damage_flag)

View File

@@ -173,8 +173,6 @@
dat += "<br><center>None detected</center>"
else
dat += "<br>[bdat]"
else
else
dat += "<A href='?src=[REF(src)];login=1'>{Log In}</A>"
var/datum/browser/popup = new(user, "med_rec", "Medical Records Console", 600, 400)
@@ -373,7 +371,6 @@
if(istype(active1.fields["photo_side"], /obj/item/photo))
var/obj/item/photo/P = active1.fields["photo_side"]
P.show(usr)
else
else if(href_list["p_stat"])
if(active1)
@@ -486,16 +483,12 @@
for(var/datum/data/record/R in GLOB.data_core.medical)
if((lowertext(R.fields["name"]) == t1 || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])))
src.active2 = R
else
//Foreach continue //goto(3229)
if(!( src.active2 ))
src.temp = text("Could not locate record [].", sanitize(t1))
else
for(var/datum/data/record/E in GLOB.data_core.general)
if((E.fields["name"] == src.active2.fields["name"] || E.fields["id"] == src.active2.fields["id"]))
src.active1 = E
else
//Foreach continue //goto(3334)
src.screen = 4
else if(href_list["print_p"])

View File

@@ -485,8 +485,6 @@ GLOBAL_LIST_EMPTY(PDAs)
//BASIC FUNCTIONS===================================
if("Refresh")//Refresh, goes to the end of the proc.
if ("Toggle_Font")
//CODE REVISION 2
font_index = (font_index + 1) % 4
@@ -710,7 +708,7 @@ GLOBAL_LIST_EMPTY(PDAs)
//LINK FUNCTIONS===================================
else//Cartridge menu linking
else //Cartridge menu linking
mode = max(text2num(href_list["choice"]), 0)
else//If not in range, can't interact or not using the pda.

View File

@@ -1007,8 +1007,7 @@
additional_desc = "Hugging this plush proves your love and devotion to all fishkind. Even space carps will respect this reverence."
/obj/item/nullrod/carp/attack_self(mob/living/user)
if(used_blessing)
else if(user.mind && (user.mind.holy_role))
if(!used_blessing && user.mind && (user.mind.holy_role))
to_chat(user, "You are blessed by Carp-Sie. Wild space carp will no longer attack you.")
user.faction |= "carp"
used_blessing = TRUE

View File

@@ -246,10 +246,12 @@
to_chat(src, span_notice("Gained [B.point_return] resources from removing \the [B]."))
qdel(B)
#define BLOB_SPREAD_COST 4 //this must also be used in the name and desc, which cannot use defines.
/mob/camera/blob/verb/expand_blob_power()
set category = "Blob"
set name = "Expand/Attack Blob ([BLOB_SPREAD_COST])"
set desc = "Attempts to create a new blob in this tile. If the tile isn't clear, instead attacks it, damaging mobs and objects and refunding [BLOB_ATTACK_REFUND] points."
set name = "Expand/Attack Blob (4)"
set desc = "Attempts to create a new blob in this tile. If the tile isn't clear, instead attacks it, damaging mobs and objects and refunding 4 points."
var/turf/T = get_turf(src)
expand_blob(T)
@@ -305,6 +307,8 @@
else
last_attack = world.time + CLICK_CD_RAPID
#undef BLOB_SPREAD_COST
/mob/camera/blob/verb/rally_spores_power()
set category = "Blob"
set name = "Rally Spores"

View File

@@ -278,6 +278,7 @@
if(BURN)
damage_amount *= fire_resist
if(CLONE)
damage_amount = damage_amount //no change
else
return 0
var/armor_protection = 0

View File

@@ -437,8 +437,8 @@
balloon_alert(artist, "not enough metal!")
return
var/list/possible_statues = list()
for(var/statues_available as anything in subtypesof(/obj/structure/bloodsucker/bloodstatue))
possible_statues[initial(statues_available)] = statues_available
for(var/obj/structure/bloodsucker/bloodstatue/statues_available as anything in subtypesof(/obj/structure/bloodsucker/bloodstatue))
possible_statues[statues_available::name] = statues_available
var/obj/structure/bloodsucker/bloodstatue/what_type = tgui_input_list(artist, "What kind of statue would you like to make?", "Artist Manual", possible_statues)
if(!do_after(artist, 10 SECONDS, src))
artist.balloon_alert(artist, "ruined!")

View File

@@ -75,17 +75,14 @@
/datum/antagonist/monkey/admin_remove(mob/admin)
var/mob/living/carbon/human/M = owner.current
if(ismonkey(M))
switch(tgui_alert(admin, "Humanize?", "Humanize", list("Yes", "No")))
if("Yes")
if(admin == M)
admin = M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSTUNS | TR_KEEPREAGENTS | TR_DEFAULTMSG)
else
M.humanize(TR_KEEPITEMS | TR_KEEPIMPLANTS | TR_KEEPORGANS | TR_KEEPDAMAGE | TR_KEEPVIRUS | TR_KEEPSTUNS | TR_KEEPREAGENTS | TR_DEFAULTMSG)
if("No")
//nothing
else
return
. = ..()
var/admin_response = tgui_alert(admin, "Humanize?", "Humanize", list("Yes", "No"))
if(admin_response != "Yes")
return ..()
if(admin == M)
admin = M.humanize(TR_KEEPITEMS|TR_KEEPIMPLANTS|TR_KEEPORGANS|TR_KEEPDAMAGE|TR_KEEPVIRUS|TR_KEEPSTUNS|TR_KEEPREAGENTS|TR_DEFAULTMSG)
else
M.humanize(TR_KEEPITEMS|TR_KEEPIMPLANTS|TR_KEEPORGANS|TR_KEEPDAMAGE|TR_KEEPVIRUS|TR_KEEPSTUNS|TR_KEEPREAGENTS|TR_DEFAULTMSG)
return ..()
/datum/antagonist/monkey/leader
name = "Monkey Leader"
@@ -94,19 +91,14 @@
/datum/antagonist/monkey/leader/admin_add(datum/mind/new_owner,mob/admin)
var/mob/living/carbon/human/H = new_owner.current
if(istype(H))
switch(tgui_alert(admin, "Monkeyize?", "Monkeyize", list("Yes", "No")))
if("Yes")
if(admin == H)
admin = H.monkeyize()
else
H.monkeyize()
if("No")
//nothing
else
return
new_owner.add_antag_datum(src)
log_admin("[key_name(admin)] made [key_name(new_owner)] a monkey leader!")
message_admins("[key_name_admin(admin)] made [key_name_admin(new_owner)] a monkey leader!")
var/admin_response = tgui_alert(admin, "Monkeyize?", "Monkeyize", list("Yes", "No"))
if(admin_response != "Yes")
return ..()
if(admin == H)
admin = H.monkeyize()
else
H.monkeyize()
return ..()
/datum/antagonist/monkey/leader/on_gain()
. = ..()

View File

@@ -245,14 +245,14 @@
if(connected_core.internal_fusion.total_moles())
for(var/gas_id in connected_core.internal_fusion.get_gases())
fusion_gasdata.Add(list(list(
"id"= initial(gas_id),
"id"= gas_id,
"amount" = round(connected_core.internal_fusion.get_moles(gas_id), 0.01),
"remove_rate" = round(connected_core.delta_fuel_list[gas_id], 0.01),
)))
else
for(var/gas_id in connected_core.internal_fusion.get_gases())
fusion_gasdata.Add(list(list(
"id"= initial(gas_id),
"id"= gas_id,
"amount" = 0,
"remove_rate" = round(connected_core.delta_fuel_list[gas_id], 0.01),
)))
@@ -261,14 +261,14 @@
if(connected_core.moderator_internal.total_moles())
for(var/gas_id in connected_core.moderator_internal.get_gases())
moderator_gasdata.Add(list(list(
"id"= initial(gas_id),
"id"= gas_id,
"amount" = round(connected_core.moderator_internal.get_moles(gas_id), 0.01),
"remove_rate" = round(connected_core.delta_mod_list[gas_id], 0.01),
)))
else
for(var/gas_id in connected_core.moderator_internal.get_gases())
moderator_gasdata.Add(list(list(
"id"= initial(gas_id),
"id"= gas_id,
"amount" = 0,
"remove_rate" = round(connected_core.delta_mod_list[gas_id], 0.01),
)))

View File

@@ -375,7 +375,7 @@
visible_message("<b>[src]</b> points at [A].", span_notice("You point at [A]."))
return TRUE
/mob/living/verb/succumb(whispered as null)
/mob/living/verb/succumb(whispered as num|null)
set hidden = TRUE
if (InCritical())
log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", LOG_ATTACK)

View File

@@ -189,6 +189,7 @@
if(BOT_NO_ROUTE)
data["modeStatus"] = "bad"
else
data["modeStatus"] = "unknown"
data["load"] = load ? load.name : null
data["destination"] = destination ? destination : null
data["home"] = home_destination

View File

@@ -2,7 +2,7 @@
//This proc is the most basic of the procs. All it does is make a new mob on the same tile and transfer over a few variables.
//Returns the new mob
//Note that this proc does NOT do MMI related stuff!
/mob/proc/change_mob_type(new_type = null, turf/location = null, new_name = null as text, delete_old_mob = FALSE)
/mob/proc/change_mob_type(new_type = null, turf/location = null, new_name = null as text|null, delete_old_mob = FALSE)
if(isnewplayer(src))
to_chat(usr, span_danger("Cannot convert players who have not entered yet."))

View File

@@ -62,8 +62,6 @@
M.adjust_eye_blur(-1)
M.adjust_blindness(-1)
switch(current_cycle)
if(1 to 20)
//nothing
if(21 to INFINITY)
if(prob(current_cycle-10))
M.cure_nearsighted(list(EYE_DAMAGE))

33
tools/ci/od_lints.dm Normal file
View File

@@ -0,0 +1,33 @@
//1000-1999
#pragma FileAlreadyIncluded error
#pragma MissingIncludedFile error
#pragma MisplacedDirective error
#pragma UndefineMissingDirective error
#pragma DefinedMissingParen error
#pragma ErrorDirective error
#pragma WarningDirective warning
#pragma MiscapitalizedDirective error
//2000-2999
#pragma SoftReservedKeyword error
#pragma DuplicateVariable error
#pragma DuplicateProcDefinition error
#pragma TooManyArguments error
#pragma PointlessParentCall error
#pragma PointlessBuiltinCall error
#pragma SuspiciousMatrixCall error
#pragma FallbackBuiltinArgument error
#pragma MalformedRange error
#pragma InvalidRange error
#pragma InvalidSetStatement error
#pragma InvalidOverride error
#pragma DanglingVarType error
#pragma MissingInterpolatedExpression error
#pragma AmbiguousResourcePath error
//3000-3999
#pragma EmptyBlock error
#pragma EmptyProc disabled
#pragma UnsafeClientAccess disabled
#pragma SuspiciousSwitchCase error
#pragma AssignmentInConditional error

View File

@@ -13,6 +13,7 @@
// END_PREFERENCES
// BEGIN_INCLUDE
#include "__odlint.dm"
#include "_maps\_basemap.dm"
#include "code\__byond_version_compat.dm"
#include "code\_compile_options.dm"

View File

@@ -28,9 +28,10 @@
/datum/wires/smartfridge/on_pulse(wire)
var/obj/machinery/smartfridge/V = holder
switch(wire)
/* //We could disable power here temporarily similar to APC short wires, but for now, it does nothing on pulse. -Firewolf34 (https://github.com/yogstation13/Yogstation/pull/6532)
if(WIRE_POWER)
//V.stat |= NOPOWER
//We could disable power here temporarily similar to APC short wires, but for now, it does nothing on pulse.
V.stat |= NOPOWER
*/
if(WIRE_DISABLE)
V.dispenser_arm = !V.dispenser_arm
if(WIRE_SHOCK)
@@ -59,4 +60,4 @@
else
V.seconds_electrified = MACHINE_ELECTRIFIED_PERMANENT
if(WIRE_SPEAKER)
V.pitches = mend
V.pitches = mend

View File

@@ -501,13 +501,13 @@
StartCooldown()
var/location = get_turf(crown.current_tar_shrines[name])
animate(owner,2.5 SECONDS,owner.color = "#280025")
if(!do_after(owner,2.5 SECONDS,owner))
animate(owner,0.5 SECONDS,owner.color = initial(owner.color))
animate(owner, time = 2.5 SECONDS, color = "#280025")
if(!do_after(owner, 2.5 SECONDS, owner))
animate(owner, time = 0.5 SECONDS, color = initial(owner.color))
return
new /obj/effect/tar_king/orb_in(get_turf(owner),owner,NORTH)
do_teleport(owner,location)
animate(owner,0.5 SECONDS,owner.color = initial(owner.color))
animate(owner, time = 0.5 SECONDS, color = initial(owner.color))
/// jungle recipes---
/datum/chemical_reaction/poultice/alt2

View File

@@ -27,6 +27,7 @@
create_typing_indicator()
bar_typing = TRUE
else if(length(temp) > 3 && findtext(temp, "Me ", 1, 5))
return
//set_typing_indicator(1)
else
bar_typing = FALSE