-
Notifications
You must be signed in to change notification settings - Fork 13
adds hopefully working macro size toggle #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
67ac905
907df44
2f86dd5
e77ec32
43c982e
caa6f02
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,33 +33,47 @@ | |
| #define SANITIZE_LIST(L) ( islist(L) ? L : list() ) | ||
| #define reverseList(L) reverseRange(L.Copy()) | ||
|
|
||
| // binary search sorted insert | ||
| // IN: Object to be inserted | ||
| // LIST: List to insert object into | ||
| // TYPECONT: The typepath of the contents of the list | ||
| // COMPARE: The variable on the objects to compare | ||
| #define BINARY_INSERT(IN, LIST, TYPECONT, COMPARE) \ | ||
| var/__BIN_CTTL = length(LIST);\ | ||
| if(!__BIN_CTTL) {\ | ||
| LIST += IN;\ | ||
| } else {\ | ||
| var/__BIN_LEFT = 1;\ | ||
| var/__BIN_RIGHT = __BIN_CTTL;\ | ||
| var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ | ||
| var/##TYPECONT/__BIN_ITEM;\ | ||
| while(__BIN_LEFT < __BIN_RIGHT) {\ | ||
| __BIN_ITEM = LIST[__BIN_MID];\ | ||
| if(__BIN_ITEM.##COMPARE <= IN.##COMPARE) {\ | ||
| __BIN_LEFT = __BIN_MID + 1;\ | ||
| } else {\ | ||
| __BIN_RIGHT = __BIN_MID;\ | ||
| /// Passed into BINARY_INSERT to compare keys | ||
| #define COMPARE_KEY __BIN_LIST[__BIN_MID] | ||
| /// Passed into BINARY_INSERT to compare values | ||
| #define COMPARE_VALUE __BIN_LIST[__BIN_LIST[__BIN_MID]] | ||
|
|
||
| /**** | ||
| * Binary search sorted insert | ||
| * Sorts low to high. | ||
| * | ||
| * * INPUT: Object to be inserted | ||
| * * LIST: List to insert object into | ||
| * * TYPECONT: The typepath of the contents of the list | ||
| * * COMPARE: The object to compare against, usualy the same as INPUT | ||
| * * COMPARISON: The variable on the objects to compare | ||
| * * COMPTYPE: How should the values be compared? Either COMPARE_KEY or COMPARE_VALUE. | ||
|
Comment on lines
-40
to
+50
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. your binary insert was out of date |
||
| */ | ||
| #define BINARY_INSERT(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \ | ||
| do {\ | ||
| var/list/__BIN_LIST = LIST;\ | ||
| var/__BIN_CTTL = length(__BIN_LIST);\ | ||
| if(!__BIN_CTTL) {\ | ||
| __BIN_LIST += INPUT;\ | ||
| } else {\ | ||
| var/__BIN_LEFT = 1;\ | ||
| var/__BIN_RIGHT = __BIN_CTTL;\ | ||
| var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ | ||
| var ##TYPECONT/__BIN_ITEM;\ | ||
| while(__BIN_LEFT < __BIN_RIGHT) {\ | ||
| __BIN_ITEM = COMPTYPE;\ | ||
| if(__BIN_ITEM.##COMPARISON <= COMPARE.##COMPARISON) {\ | ||
| __BIN_LEFT = __BIN_MID + 1;\ | ||
| } else {\ | ||
| __BIN_RIGHT = __BIN_MID;\ | ||
| };\ | ||
| __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ | ||
| };\ | ||
| __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ | ||
| __BIN_ITEM = COMPTYPE;\ | ||
| __BIN_MID = __BIN_ITEM.##COMPARISON > COMPARE.##COMPARISON ? __BIN_MID : __BIN_MID + 1;\ | ||
| __BIN_LIST.Insert(__BIN_MID, INPUT);\ | ||
| };\ | ||
| __BIN_ITEM = LIST[__BIN_MID];\ | ||
| __BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\ | ||
| LIST.Insert(__BIN_MID, IN);\ | ||
| } | ||
| } while(FALSE) | ||
|
|
||
| //Returns a list in plain english as a string | ||
| /proc/english_list(list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" ) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -161,6 +161,35 @@ | |
| return call(delegate)(arglist(calling_arguments)) | ||
| return call(object, delegate)(arglist(calling_arguments)) | ||
|
|
||
| /** | ||
| * Invoke this callback and crash if it sleeps. | ||
| * | ||
| * * Use when a callback should never sleep, as call() cannot be verified by static analysis. | ||
| * * Do not use in performance critical code. This wraps calls more aggressively than InvokeAsync(). | ||
| * * `null` is returned if the call sleeps. | ||
| * | ||
| * The specific use case here is an async call where: | ||
| * | ||
| * * It's always invalid behavior for a callback to sleep. | ||
| * * The caller should be protected (caller shouldn't be interrupted by the sleep). | ||
| * | ||
| * This allows enforcement of the above invariants by loudly runtiming and bringing attention to the issue, | ||
| * as opposed to the usual way of compile checking it (which we can't because this is a reflection-based call to an arbitrary proc). | ||
| */ | ||
| /datum/callback/proc/invoke_no_sleep(...) | ||
| #define CALLBACK_SLEEP_SENTINEL "---!!SOMEBULLSHITSTRINGYOUDEFINITELYWONTSEEANYWHEREELSE----!!!" | ||
| . = CALLBACK_SLEEP_SENTINEL | ||
| . = invoke_no_sleep_call(arglist(args)) | ||
| if(. == CALLBACK_SLEEP_SENTINEL) | ||
| . = null | ||
| CRASH("Callback [src] slept on a no-sleeping invoke.") | ||
| #undef CALLBACK_SLEEP_SENTINEL | ||
|
|
||
| /datum/callback/proc/invoke_no_sleep_call(...) | ||
| PRIVATE_PROC(TRUE) | ||
| set waitfor = FALSE | ||
|
Comment on lines
+179
to
+190
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. protective sentinel call to prevent sleeping; if the hooks used in the component slept it would literally detonate SSoverlays, you do not want this |
||
| . = Invoke(arglist(args)) | ||
|
|
||
| /** | ||
| Helper datum for the select callbacks proc | ||
| */ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,126 @@ | ||
|
|
||
| /** | ||
| * This mildly unhinged-sounding components allows arbitrarily having a somewhat-automatically- | ||
| * updating 'override' image for one's self. This solves the issue of BYOND only allowing | ||
| * one override image per atom per client, but, there's a catch. | ||
| * | ||
| * * The API for this isn't laggy, per se, but isn't high-perforamnce either. This is | ||
| * a lazy component to fulfil a purpose. If you are reading this in the future and | ||
| * this component is visible on profiler's CPU rankings and it's a problem, | ||
| * look into proper managed rendering via planes / vis contents / byond intrinsics / | ||
| * refactoring this component to be update-event based, not rebuild based. | ||
| * * The reason this is inefficient is because all callbacks are invoked every time | ||
| * we need an update. This is not great; there's no way to selective-update. | ||
| * * The API to use this is LoadComponent(). Yeah, this isn't amazing. | ||
| * * The hook API uses string keys. This is because I don't really trust featurecoders | ||
| * with handling raw callback references. Sorry! Strings, at the least, are pooled by | ||
| * BYOND for you. | ||
| * * One override per atom per client still applies; this can be trampled by misbehaving code | ||
| * elsewhere, like using alternate appearances that show to self while this is active. | ||
| * There's nothing I can do about that; having an actually generate alternate-appearance-override | ||
| * system is the realm of a *massive* atom HUD refactor, which I am not able to do in just 24 hours. | ||
| */ | ||
| /datum/component/self_image_override | ||
| /// associative list key to /datum/component_self_image_override_entry; lower priority is applied first | ||
| var/list/alter_entries | ||
| /// our rendering image | ||
| var/image/renderer | ||
|
|
||
| /datum/component/self_image_override/Initialize() | ||
| if(!ismob(parent)) | ||
| return COMPONENT_INCOMPATIBLE | ||
| . = ..() | ||
| if(. == COMPONENT_INCOMPATIBLE) | ||
| return | ||
|
|
||
| /datum/component/self_image_override/RegisterWithParent() | ||
| RegisterSignal(parent, COMSIG_ATOM_COMPILED_OVERLAYS, PROC_REF(on_overlay_update)) | ||
| renderer = new | ||
| renderer.loc = parent | ||
| ensure_image_is_on_client() | ||
| update() | ||
|
|
||
| /datum/component/self_image_override/UnregisterFromParent() | ||
| UnregisterSignal(parent, COMSIG_ATOM_COMPILED_OVERLAYS) | ||
| if(renderer) | ||
| var/mob/mob_parent = parent | ||
| if(mob_parent.client) | ||
| mob_parent.client -= renderer | ||
| renderer.loc = null | ||
| // try not to harddel; byond scans active procs faster. | ||
| var/image/unreferencing = renderer | ||
| renderer = null | ||
| qdel(renderer) | ||
|
|
||
| /** | ||
| * Adds an alteration hook with a given priority. | ||
| * * This hook is ran on every update. | ||
| * * This does **not** trigger an immediate update. | ||
| * * If the key already exists, the old hook will be overwritten. | ||
| * * You are responsible for ensuring the callback is valid for the duration of the alteration's | ||
| * lifetime on this component. If the callback is invalidated or its delegate is, runtimes | ||
| * will occur every update and this is very, very bad. | ||
| * * Callback hooks **cannot sleep under any circumstances.** Doing so will blow things up, like | ||
| * for example the overlay subsystem. This is enforced with an `invoke_no_sleep()`. | ||
| * | ||
| * @params | ||
| * * key - string key to register hook under | ||
| * * hook - the callback hook, which will be called with (mob/host_mob, mutable_appearance/modifying) | ||
| * * priority - priority to register under; lower runs first. | ||
| */ | ||
| /datum/component/self_image_override/proc/add_alteration_hook(key, datum/callback/hook, priority) | ||
| ASSERT(isnum(priority)) | ||
| ASSERT(istype(hook)) | ||
| if(alter_entries[key]) | ||
| remove_alteration_hook(key) | ||
| var/datum/component_self_image_override_entry/entry = new(key, hook, priority) | ||
| BINARY_INSERT(entry, alter_entries, /datum/component_self_image_override_entry, entry, priority, COMPARE_KEY) | ||
|
|
||
| /datum/component/self_image_override/proc/remove_alteration_hook(key) | ||
| alter_entries -= key | ||
| if(!length(alter_entries)) | ||
| addtimer(CALLBACK(src, PROC_REF(auto_gc_if_empty)), 0) | ||
|
|
||
| /datum/component/self_image_override/proc/auto_gc_if_empty() | ||
| if(!length(alter_entries)) | ||
| qdel(src) | ||
|
|
||
| /datum/component/self_image_override/proc/on_overlay_update(datum/source) | ||
| PRIVATE_PROC(TRUE) | ||
| SIGNAL_HANDLER | ||
| update() | ||
|
|
||
| /datum/component/self_image_override/proc/update() | ||
| if(!renderer) | ||
| return | ||
| var/mob/our_parent_mob = parent | ||
| var/mutable_appearance/mutating = new(our_parent_mob) | ||
| for(var/datum/component_self_image_override_entry/entry as anything in alter_entries) | ||
| // just because i expect featurecoders to use this code this contains a sanity check | ||
| // to make sure the callback's target object | ||
| var/datum/callback/entry_cb = entry.callback | ||
| if(entry_cb.object != GLOBAL_PROC && QDELETED(entry_cb.object)) | ||
| alter_entries -= entry | ||
| // if you are seeing this this is **always** incorrect behavior. | ||
| stack_trace("self image override on mob [REF(our_parent_mob)] contained an alter hook callback with key [entry.key] with a callback targeting a qdeleted object [entry_cb.object]") | ||
| continue | ||
| entry_cb.invoke_no_sleep(our_parent_mob, mutating) | ||
| renderer.appearance = mutating | ||
|
|
||
| /datum/component/self_image_override/proc/ensure_image_is_on_client() | ||
| // it should not be necessary to trigger this proc too much but most codebases | ||
| // are lazy and just clear images all the time, so, just call this lol | ||
| var/mob/our_parent_mob = parent | ||
| var/client/maybe_client = our_parent_mob.client | ||
| maybe_client.images |= renderer | ||
|
|
||
| /// quite a mouthful | ||
| /datum/component_self_image_override_entry | ||
| var/key | ||
| var/datum/callback/callback | ||
| var/priority | ||
|
|
||
| /datum/component_self_image_override_entry/New(key, datum/callback/callback, priority) | ||
| src.key = key | ||
| src.callback = callback | ||
| src.priority = priority |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needed a signal hook to know when to re-render