Files
Bubberstation/code/datums/components/bakeable.dm
Timberpoes ec78c0f729 Fixes an issue with ovens that generates infinite food. (#78372)
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->

## About The Pull Request

Introduced by #78322


![image](https://github.com/tgstation/tgstation/assets/24975989/08cefb52-7247-433d-851c-ab59a3d890d4)

Using the wrong var (uwupsie, path var instead of object var) meant an
early runtime. The early runtime meant the code path never qdel'd parent
on the bakeable reagent.


![image](https://github.com/tgstation/tgstation/assets/24975989/851161fd-a79e-4140-97ba-5ca567213413)

So every oven process, the dough would cook into bread without being
consumed.
Every oven process, the bread would cook into badrecipe without being
consumed.
Every often process, the badrecipe would cook into more badrecipe
without being consumed.

Every time this happened, it made ash.

After about ~250 oven processing ticks on Terry, 17k ash was Initialised
in a certain kitchen and it would crash anyone that walked near it.


![image](https://github.com/tgstation/tgstation/assets/24975989/c8a67d60-d86e-4ef4-9461-006822cf69d0)

(Yes, I crashed my client opening this list)

Replication steps are as simple as... Putting dough in an oven on a
tray, closing the oven and waiting 10 minutes for the kitchen to be a
client crash zone. Also lags the server due to the code paths involved.
Like 17k ash creating 129 million proc calls to replace_decal.


![image](https://github.com/tgstation/tgstation/assets/24975989/52b06984-b9ff-4450-aaf9-de3e84257077)

![image](https://github.com/tgstation/tgstation/assets/24975989/9135634b-4aaf-4954-888c-3b93ba2899c9)

That should probably be optimised at some point.

Due to how destructive this bug is with client crashes and significant
server lag, plus the VERY strong potential for this to happen purely by
accident (just putting dough in the oven and forgetting) I'm marking
this high priority.

<!-- Describe The Pull Request. Please be sure every change is
documented or this can delay review and even discourage maintainers from
merging your PR! -->

## Why It's Good For The Game

Forgetting to take dough out of the oven no longer progresses the server
to a crash-worthy state.

<!-- Argue for the merits of your changes and how they benefit the game,
especially if they are controversial and/or far reaching. If you can't
actually explain WHY what you are doing will improve the game, then it
probably isn't good for the game in the first place. -->

## Changelog

<!-- If your PR modifies aspects of the game that can be concretely
observed by players or admins you should add a changelog. If your change
does NOT meet this description, remove this section. Be sure to properly
mark your PRs to prevent unnecessary GBP loss. You can read up on GBP
and it's effects on PRs in the tgstation guides for contributors. Please
note that maintainers freely reserve the right to remove and add tags
should they deem it appropriate. You can attempt to finagle the system
all you want, but it's best to shoot for clear communication right off
the bat. -->

🆑
fix: Forgetting to take dough out of the oven no longer progresses the
server to a crash-worthy state with infinite bread and ash and burned
food products for all.
/🆑

<!-- Both 🆑's are required for the changelog to work! You can put
your name to the right of the first 🆑 if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->
2023-09-16 00:35:38 +00:00

110 lines
4.4 KiB
Plaintext

///This component indicates this object can be baked in an oven.
/datum/component/bakeable
dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS // So you can change bake results with various cookstuffs
///Result atom type of baking this object
var/atom/bake_result
///Amount of time required to bake the food
var/required_bake_time = 2 MINUTES
///Is this a positive bake result?
var/positive_result = TRUE
///Time spent baking so far
var/current_bake_time = 0
/// REF() to the mind which placed us in an oven
var/who_baked_us
/datum/component/bakeable/Initialize(bake_result, required_bake_time, positive_result, use_large_steam_sprite)
. = ..()
if(!isitem(parent)) //Only items support baking at the moment
return COMPONENT_INCOMPATIBLE
src.bake_result = bake_result
src.required_bake_time = required_bake_time
src.positive_result = positive_result
// Inherit the new values passed to the component
/datum/component/bakeable/InheritComponent(datum/component/bakeable/new_comp, original, bake_result, required_bake_time, positive_result, use_large_steam_sprite)
if(!original)
return
if(bake_result)
src.bake_result = bake_result
if(required_bake_time)
src.required_bake_time = required_bake_time
if(positive_result)
src.positive_result = positive_result
/datum/component/bakeable/RegisterWithParent()
RegisterSignal(parent, COMSIG_ITEM_OVEN_PLACED_IN, PROC_REF(on_baking_start))
RegisterSignal(parent, COMSIG_ITEM_OVEN_PROCESS, PROC_REF(on_bake))
RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine))
/datum/component/bakeable/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_ITEM_OVEN_PLACED_IN, COMSIG_ITEM_OVEN_PROCESS, COMSIG_ATOM_EXAMINE))
/// Signal proc for [COMSIG_ITEM_OVEN_PLACED_IN] when baking starts (parent enters an oven)
/datum/component/bakeable/proc/on_baking_start(datum/source, atom/used_oven, mob/baker)
SIGNAL_HANDLER
if(baker && baker.mind)
who_baked_us = REF(baker.mind)
///Ran every time an item is baked by something
/datum/component/bakeable/proc/on_bake(datum/source, atom/used_oven, seconds_per_tick = 1)
SIGNAL_HANDLER
// Let our signal know if we're baking something good or ... burning something
var/baking_result = positive_result ? COMPONENT_BAKING_GOOD_RESULT : COMPONENT_BAKING_BAD_RESULT
current_bake_time += seconds_per_tick * 10 //turn it into ds
if(current_bake_time >= required_bake_time)
finish_baking(used_oven)
return COMPONENT_HANDLED_BAKING | baking_result
///Ran when an object finished baking
/datum/component/bakeable/proc/finish_baking(atom/used_oven)
var/atom/original_object = parent
var/obj/item/plate/oven_tray/used_tray = original_object.loc
var/atom/baked_result = new bake_result(used_tray)
baked_result.reagents.clear_reagents()
original_object.reagents?.trans_to(baked_result, original_object.reagents.total_volume)
if(who_baked_us)
ADD_TRAIT(baked_result, TRAIT_FOOD_CHEF_MADE, who_baked_us)
if(original_object.custom_materials)
baked_result.set_custom_materials(original_object.custom_materials, 1)
baked_result.pixel_x = original_object.pixel_x
baked_result.pixel_y = original_object.pixel_y
used_tray.AddToPlate(baked_result)
if(positive_result)
used_oven.visible_message(span_notice("You smell something great coming from [used_oven]."), blind_message = span_notice("You smell something great..."))
BLACKBOX_LOG_FOOD_MADE(baked_result.type)
else
used_oven.visible_message(span_warning("You smell a burnt smell coming from [used_oven]."), blind_message = span_warning("You smell a burnt smell..."))
SEND_SIGNAL(parent, COMSIG_ITEM_BAKED, baked_result)
qdel(parent)
///Gives info about the items baking status so you can see if its almost done
/datum/component/bakeable/proc/on_examine(atom/source, mob/user, list/examine_list)
SIGNAL_HANDLER
if(!current_bake_time) //Not baked yet
if(positive_result)
if(initial(bake_result.gender) == PLURAL)
examine_list += span_notice("[parent] can be [span_bold("baked")] into some [initial(bake_result.name)].")
else
examine_list += span_notice("[parent] can be [span_bold("baked")] into \a [initial(bake_result.name)].")
return
if(positive_result)
if(current_bake_time <= required_bake_time * 0.75)
examine_list += span_notice("[parent] probably needs to be baked a bit longer!")
else if(current_bake_time <= required_bake_time)
examine_list += span_notice("[parent] seems to be almost finished baking!")
else
examine_list += span_danger("[parent] should probably not be put in the oven.")