Skip to content

Commit

Permalink
autowiki port and gun autowiki (#4107)
Browse files Browse the repository at this point in the history
MANY thanks to mothblocks of tg on
tgstation/tgstation#64417 for this, it's very
cool!

this ports the autowiki (https://tgstation13.org/wiki/Guide_to_autowiki)
and can be used to generate wiki page templates instead of monotonously
updating damage values Forever!

this is a weird one to changelog so:

:cl: mothblocks, harry
add: added the backend functionality for autowiki, alongside automating
much of the work of maintaining guns on the wiki
/:cl:
  • Loading branch information
harryob committed Aug 12, 2023
1 parent 2940c48 commit e85d66a
Show file tree
Hide file tree
Showing 12 changed files with 1,447 additions and 2 deletions.
52 changes: 52 additions & 0 deletions .github/workflows/autowiki.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Autowiki
on:
schedule:
- cron: "5 4 * * *"
workflow_dispatch:
permissions:
contents: read

jobs:
autowiki:
runs-on: ubuntu-20.04
steps:
- name: "Check for AUTOWIKI_USERNAME"
id: secrets_set
env:
ENABLER_SECRET: ${{ secrets.AUTOWIKI_USERNAME }}
run: |
unset SECRET_EXISTS
if [ -n "$ENABLER_SECRET" ]; then SECRET_EXISTS=true ; fi
echo "SECRETS_ENABLED=$SECRET_EXISTS" >> $GITHUB_OUTPUT
- name: Checkout
if: steps.secrets_set.outputs.SECRETS_ENABLED
uses: actions/checkout@v3
- name: Restore BYOND cache
if: steps.secrets_set.outputs.SECRETS_ENABLED
uses: actions/cache@v3
with:
path: ~/BYOND
key: ${{ runner.os }}-byond-${{ secrets.CACHE_PURGE_KEY }}
- name: Install rust-g
if: steps.secrets_set.outputs.SECRETS_ENABLED
run: |
sudo dpkg --add-architecture i386
sudo apt update || true
sudo apt install -o APT::Immediate-Configure=false libssl1.1:i386
bash tools/ci/install_rust_g.sh
- name: Compile and generate Autowiki files
if: steps.secrets_set.outputs.SECRETS_ENABLED
run: |
bash tools/ci/install_byond.sh
source $HOME/BYOND/byond/bin/byondsetup
tools/build/build --ci autowiki
- name: Run Autowiki
if: steps.secrets_set.outputs.SECRETS_ENABLED
env:
USERNAME: ${{ secrets.AUTOWIKI_USERNAME }}
PASSWORD: ${{ secrets.AUTOWIKI_PASSWORD }}
run: |
cd tools/autowiki
npm install
cd ../..
node tools/autowiki/autowiki.js data/autowiki_edits.txt data/autowiki_files/
6 changes: 5 additions & 1 deletion code/game/world.dm
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ var/list/reboot_sfx = file2list("config/reboot_sfx.txt")

var/testing_locally = (world.params && world.params["local_test"])
var/running_tests = (world.params && world.params["run_tests"])
#ifdef UNIT_TESTS
#if defined(AUTOWIKI) || defined(UNIT_TESTS)
running_tests = TRUE
#endif
// Only do offline sleeping when the server isn't running unit tests or hosting a local dev test
Expand All @@ -84,6 +84,10 @@ var/list/reboot_sfx = file2list("config/reboot_sfx.txt")
HandleTestRun()
#endif

#ifdef AUTOWIKI
setup_autowiki()
#endif

update_status()

//Scramble the coords obsfucator
Expand Down
36 changes: 36 additions & 0 deletions code/modules/autowiki/autowiki.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/// When the `AUTOWIKI` define is enabled, will generate an output file for tools/autowiki/autowiki.js to consume.
/// Autowiki code intentionally still *exists* even without the define, to ensure developers notice
/// when they break it immediately, rather than until CI or worse, call time.
#if defined(AUTOWIKI) || defined(UNIT_TESTS)
/proc/setup_autowiki()
Master.sleep_offline_after_initializations = FALSE
UNTIL(SSticker.current_state == GAME_STATE_PREGAME)

//trigger things to run the whole process
SSticker.request_start()
CONFIG_SET(number/round_end_countdown, 0)
SSticker.OnRoundstart(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(generate_autowiki)))

/proc/generate_autowiki()
var/output = generate_autowiki_output()
rustg_file_write(output, "data/autowiki_edits.txt")
qdel(world)
#endif

/// Returns a string of the autowiki output file
/proc/generate_autowiki_output()
var/total_output = ""

for (var/datum/autowiki/autowiki_type as anything in subtypesof(/datum/autowiki))
var/datum/autowiki/autowiki = new autowiki_type
var/output = autowiki.generate()

if (!istext(output))
CRASH("[autowiki_type] does not generate a proper output!")

total_output += json_encode(list(
"title" = autowiki.page,
"text" = output,
)) + "\n"

return total_output
54 changes: 54 additions & 0 deletions code/modules/autowiki/pages/_page.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/// A representation of an automated wiki page.
/datum/autowiki
/// The page on the wiki to be replaced.
/// This should never be a user-facing page, like "Guide to circuits".
/// It should always be a template that only Autowiki should touch.
/// For example: "Template:Autowiki/CircuitInfo".
var/page

/// Override and return the new text of the page.
/// This proc can be impure, usually to call `upload_file`.
/datum/autowiki/proc/generate()
SHOULD_CALL_PARENT(FALSE)
CRASH("[type] does not implement generate()!")

/// Generates an auto formatted template user.
/// Your autowiki should ideally be a *lot* of these.
/// It lets wiki editors edit it much easier later, without having to enter repo.
/// Parameters will be passed in by name. That means your template should expect
/// something that looks like `{{ Autowiki_Circuit|name=Combiner|description=This combines }}`
/// Lists, which must be array-like (no keys), will be turned into a flat list with their key and a number,
/// such that list("food" = list("fruit", "candy")) -> food1=fruit|food2=candy
/datum/autowiki/proc/include_template(name, parameters)
var/template_text = "{{[name]"

var/list/prepared_parameters = list()
for (var/key in parameters)
var/value = parameters[key]
if (islist(value))
for (var/index in 1 to length(value))
prepared_parameters["[key][index]"] = "[value[index]]"
else
prepared_parameters[key] = value

for (var/parameter_name in prepared_parameters)
template_text += "|[parameter_name]="
template_text += "[prepared_parameters[parameter_name]]"

template_text += "}}"

return template_text

/// Takes an icon and uploads it to Autowiki-name.png.
/// Do your best to make sure this is unique, so it doesn't clash with other autowiki icons.
/datum/autowiki/proc/upload_icon(icon/icon, name)
// Fuck you
if (IsAdminAdvancedProcCall())
return

fcopy(icon, "data/autowiki_files/[name].png")

/// Escape a parameter such that it can be correctly put inside a wiki output
/datum/autowiki/proc/escape_value(parameter)
// | is a special character in MediaWiki, and must be escaped by...using another template.
return replacetextEx(parameter, "|", "{{!}}")
118 changes: 118 additions & 0 deletions code/modules/autowiki/pages/guns.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/datum/autowiki/guns
page = "Template:Autowiki/Content/GunData"


/datum/autowiki/guns/generate()
var/output = ""

var/list/gun_to_ammo = list()

for(var/obj/item/ammo_magazine/typepath as anything in subtypesof(/obj/item/ammo_magazine) - subtypesof(/obj/item/ammo_magazine/internal))
LAZYADD(gun_to_ammo[initial(typepath.gun_type)], typepath)

for(var/typepath in sort_list(subtypesof(/obj/item/weapon/gun), GLOBAL_PROC_REF(cmp_typepaths_asc)))
var/obj/item/weapon/gun/generating_gun = new typepath()

var/filename = SANITIZE_FILENAME(escape_value(format_text(generating_gun.name)))

var/list/gun_data = generating_gun.ui_data()

var/list/valid_mag_types = list()
for(var/path in gun_to_ammo)
if(!istype(generating_gun, path))
continue

valid_mag_types += gun_to_ammo[path]

var/ammo = ""
var/damage_table = ""
for(var/ammo_typepath in valid_mag_types)
var/obj/item/ammo_magazine/generating_mag = new ammo_typepath()

var/ammo_filename = SANITIZE_FILENAME(escape_value(format_text(generating_mag.name)))

if(!fexists("data/autowiki_files/[ammo_filename].png"))
upload_icon(getFlatIcon(generating_mag, no_anim = TRUE), ammo_filename)

var/datum/ammo/current_ammo = GLOB.ammo_list[generating_mag.default_ammo]

ammo += include_template("Autowiki/AmmoMagazine", list(
"icon" = escape_value(ammo_filename),
"name" = escape_value(generating_mag.name),
"capacity" = escape_value(generating_mag.max_rounds),
"damage" = escape_value(current_ammo.damage),
"max_range" = escape_value(current_ammo.max_range),
"fall_off" = escape_value(current_ammo.damage_falloff),
"penetration" = escape_value(current_ammo.penetration),
"punch" = escape_value(current_ammo.pen_armor_punch),
))

generating_gun.current_mag = generating_mag

var/list/gun_ammo_data = generating_gun.ui_data()
var/list/armor_data = list()

var/iterator = 1
for(var/header in gun_ammo_data["damage_armor_profile_headers"])
var/damage = gun_ammo_data["damage_armor_profile_marine"][iterator]
armor_data["armor-[header]"] = damage
iterator++

var/list/damage = list("ammo_name" = escape_value(generating_mag.name))
damage += armor_data

damage_table += include_template("Autowiki/DamageVersusArmorRow", damage)

qdel(generating_mag)

gun_data["ammo_types"] = ammo
gun_data["damage_table"] = damage_table

var/list/attachments_by_slot = list()
for(var/obj/item/attachable/attachment_typepath as anything in generating_gun.attachable_allowed)
LAZYADD(attachments_by_slot[capitalize(initial(attachment_typepath.slot))], attachment_typepath)

var/attachments = ""
for(var/slot in attachments_by_slot)
var/list/attachments_in_slot = ""

for(var/attachment_typepath in attachments_by_slot[slot])
var/obj/item/attachable/generating_attachment = new attachment_typepath()

var/attachment_filename = SANITIZE_FILENAME(escape_value(format_text(generating_attachment.name)))

if(!fexists("data/autowiki_files/[attachment_filename].png"))
upload_icon(getFlatIcon(generating_attachment, no_anim = TRUE), attachment_filename)

attachments_in_slot += include_template("Autowiki/AvailableAttachment", list(
"icon" = escape_value(attachment_filename),
"name" = escape_value(generating_attachment.name),
))

qdel(generating_attachment)

attachments += include_template("Autowiki/AttachmentsBySlot", list(
"slot" = escape_value(slot),
"attachments" = attachments_in_slot,
))
gun_data["attachments"] = attachments


upload_icon(getFlatIcon(generating_gun, no_anim = TRUE), filename)
gun_data["icon"] = filename

output += include_template("Autowiki/Gun", gun_data)

qdel(generating_gun)

return output

/datum/autowiki/guns/proc/wiki_sanitize_assoc(list/sanitizing_list)
var/list/sanitized = list()

for(var/key in sanitizing_list)
var/value = sanitizing_list[key]

sanitized[escape_value(key)] = escape_value(value)

return sanitized
2 changes: 1 addition & 1 deletion code/modules/projectiles/gun.dm
Original file line number Diff line number Diff line change
Expand Up @@ -616,7 +616,7 @@ As sniper rifles have both and weapon mods can change them as well. ..() deals w
if(in_chamber && in_chamber.ammo)
in_ammo = in_chamber.ammo
else if(current_mag && current_mag.current_rounds > 0)
if(istype(current_mag) && current_mag.chamber_contents[current_mag.chamber_position] != "empty")
if(istype(current_mag) && length(current_mag.chamber_contents) && current_mag.chamber_contents[current_mag.chamber_position] != "empty")
in_ammo = GLOB.ammo_list[current_mag.chamber_contents[current_mag.chamber_position]]
if(!istype(in_ammo))
in_ammo = GLOB.ammo_list[current_mag.default_ammo]
Expand Down
1 change: 1 addition & 0 deletions code/modules/unit_tests/_unit_tests.dm
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
/// A trait source when adding traits through unit tests
#define TRAIT_SOURCE_UNIT_TESTS "unit_tests"

#include "autowiki.dm"
#include "create_and_destroy.dm"
#include "focus_only_tests.dm"
#include "missing_icons.dm"
Expand Down
35 changes: 35 additions & 0 deletions code/modules/unit_tests/autowiki.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/// Tests that all autowikis generate something without runtiming
/datum/unit_test/autowiki

/datum/unit_test/autowiki/Run()
TEST_ASSERT(istext(generate_autowiki_output()), "generate_autowiki_output() did not finish successfully!")

/// Test that `include_template` produces reasonable results
/datum/unit_test/autowiki_include_template

/datum/unit_test/autowiki_include_template/Run()
var/datum/autowiki/autowiki_api = new

TEST_ASSERT_EQUAL( \
autowiki_api.include_template("Template"), \
"{{Template}}", \
"Basic template did not format correctly" \
)

TEST_ASSERT_EQUAL( \
autowiki_api.include_template("Template", list("name" = "Mothblocks")), \
"{{Template|name=Mothblocks}}", \
"Template with basic arguments did not format correctly" \
)

TEST_ASSERT_EQUAL( \
autowiki_api.include_template("Template", list("name" = autowiki_api.escape_value("P|peline"))), \
"{{Template|name=P{{!}}peline}}", \
"Template with escaped arguments did not format correctly" \
)

TEST_ASSERT_EQUAL( \
autowiki_api.include_template("Template", list("food" = list("fruit", "candy"))), \
"{{Template|food1=fruit|food2=candy}}", \
"Template with array arguments did not format correctly" \
)
3 changes: 3 additions & 0 deletions colonialmarines.dme
Original file line number Diff line number Diff line change
Expand Up @@ -1400,6 +1400,9 @@
#include "code\modules\asset_cache\assets\vending.dm"
#include "code\modules\asset_cache\transports\asset_transport.dm"
#include "code\modules\asset_cache\transports\webroot_transport.dm"
#include "code\modules\autowiki\autowiki.dm"
#include "code\modules\autowiki\pages\_page.dm"
#include "code\modules\autowiki\pages\guns.dm"
#include "code\modules\buildmode\bm-mode.dm"
#include "code\modules\buildmode\buildmode.dm"
#include "code\modules\buildmode\buttons.dm"
Expand Down
Loading

0 comments on commit e85d66a

Please sign in to comment.