mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-26 17:11:52 +00:00
Basically, they key difference between StonedMC and CarnMC is that when multiple ticks want to run at the same byond tick, we divvy up the tick between the subsystems, rather then allow one subsystem to hog it all. The key difference between StonedMC and GoonPS is that we allow the subsystems to tell us how to divvy up the tick using flags and priority. The new SS_ flags allows us to select behaviors that used to be piggybacked as side effects of dynamic wait or default but sometimes unneeded behavior. Dynamic wait is 100% gone, lower priority and SS_BACKGROUND are better more refined ways of doing this when combined with MC_TICK_CHECK I have by design never looked at the inners of goonPS, so this is all original code but I know it uses two loops because of comments by goon devs on reddit threads, that design didn't make sense before, but when I can tell a SS how much of a byond tick it is allowed to have, knowing how many need to run this tick is helpful I also know a bit more about how it works from piecing together comments in #vgstation. Detailed list of changes: Subsystems now have flags, allowing fine grain control over things like rather or not it processes, inits, rather it's wait is how long between runs (post run timing) or how long between starts, and rather or not late fires should cause the next fire to be earlier. Mc now has two loops One loop handles queuing shit, one loop handles running shit. MC now splits up tick allotment rather than first come first serve Subsystems can even request a bigger share using higher priorities. (It will even resume subsystems it paused if other subsystems hadn't used as much as it predicted they might need) Default fps is now 20 This is related enough to the MC and it's a change that's really long since over due All code oddities are most likely to be necessities to lower overhead on the mc since it runs every tick
308 lines
8.9 KiB
Plaintext
308 lines
8.9 KiB
Plaintext
var/datum/subsystem/vote/SSvote
|
|
|
|
/datum/subsystem/vote
|
|
name = "Vote"
|
|
wait = 10
|
|
|
|
flags = SS_FIRE_IN_LOBBY|SS_KEEP_TIMING|SS_NO_INIT
|
|
|
|
var/initiator = null
|
|
var/started_time = null
|
|
var/time_remaining = 0
|
|
var/mode = null
|
|
var/question = null
|
|
var/list/choices = list()
|
|
var/list/voted = list()
|
|
var/list/voting = list()
|
|
var/list/generated_actions = list()
|
|
|
|
/datum/subsystem/vote/New()
|
|
NEW_SS_GLOBAL(SSvote)
|
|
|
|
/datum/subsystem/vote/fire() //called by master_controller
|
|
if(mode)
|
|
time_remaining = round((started_time + config.vote_period - world.time)/10)
|
|
|
|
if(time_remaining < 0)
|
|
result()
|
|
for(var/client/C in voting)
|
|
C << browse(null, "window=vote;can_close=0")
|
|
reset()
|
|
else
|
|
var/datum/browser/client_popup
|
|
for(var/client/C in voting)
|
|
client_popup = new(C, "vote", "Voting Panel")
|
|
client_popup.set_window_options("can_close=0")
|
|
client_popup.set_content(interface(C))
|
|
client_popup.open(0)
|
|
|
|
|
|
/datum/subsystem/vote/proc/reset()
|
|
initiator = null
|
|
time_remaining = 0
|
|
mode = null
|
|
question = null
|
|
choices.Cut()
|
|
voted.Cut()
|
|
voting.Cut()
|
|
remove_action_buttons()
|
|
|
|
/datum/subsystem/vote/proc/get_result()
|
|
//get the highest number of votes
|
|
var/greatest_votes = 0
|
|
var/total_votes = 0
|
|
for(var/option in choices)
|
|
var/votes = choices[option]
|
|
total_votes += votes
|
|
if(votes > greatest_votes)
|
|
greatest_votes = votes
|
|
//default-vote for everyone who didn't vote
|
|
if(!config.vote_no_default && choices.len)
|
|
var/non_voters = (clients.len - total_votes)
|
|
if(non_voters > 0)
|
|
if(mode == "restart")
|
|
choices["Continue Playing"] += non_voters
|
|
if(choices["Continue Playing"] >= greatest_votes)
|
|
greatest_votes = choices["Continue Playing"]
|
|
else if(mode == "gamemode")
|
|
if(master_mode in choices)
|
|
choices[master_mode] += non_voters
|
|
if(choices[master_mode] >= greatest_votes)
|
|
greatest_votes = choices[master_mode]
|
|
//get all options with that many votes and return them in a list
|
|
. = list()
|
|
if(greatest_votes)
|
|
for(var/option in choices)
|
|
if(choices[option] == greatest_votes)
|
|
. += option
|
|
return .
|
|
|
|
/datum/subsystem/vote/proc/announce_result()
|
|
var/list/winners = get_result()
|
|
var/text
|
|
if(winners.len > 0)
|
|
if(question)
|
|
text += "<b>[question]</b>"
|
|
else
|
|
text += "<b>[capitalize(mode)] Vote</b>"
|
|
for(var/i=1,i<=choices.len,i++)
|
|
var/votes = choices[choices[i]]
|
|
if(!votes)
|
|
votes = 0
|
|
text += "\n<b>[choices[i]]:</b> [votes]"
|
|
if(mode != "custom")
|
|
if(winners.len > 1)
|
|
text = "\n<b>Vote Tied Between:</b>"
|
|
for(var/option in winners)
|
|
text += "\n\t[option]"
|
|
. = pick(winners)
|
|
text += "\n<b>Vote Result: [.]</b>"
|
|
else
|
|
text += "\n<b>Did not vote:</b> [clients.len-voted.len]"
|
|
else
|
|
text += "<b>Vote Result: Inconclusive - No Votes!</b>"
|
|
log_vote(text)
|
|
remove_action_buttons()
|
|
world << "\n<font color='purple'>[text]</font>"
|
|
return .
|
|
|
|
/datum/subsystem/vote/proc/result()
|
|
. = announce_result()
|
|
var/restart = 0
|
|
if(.)
|
|
switch(mode)
|
|
if("restart")
|
|
if(. == "Restart Round")
|
|
restart = 1
|
|
if("gamemode")
|
|
if(master_mode != .)
|
|
world.save_mode(.)
|
|
if(ticker && ticker.mode)
|
|
restart = 1
|
|
else
|
|
master_mode = .
|
|
if(restart)
|
|
var/active_admins = 0
|
|
for(var/client/C in admins)
|
|
if(!C.is_afk() && check_rights_for(C, R_SERVER))
|
|
active_admins = 1
|
|
break
|
|
if(!active_admins)
|
|
world.Reboot("Restart vote successful.", "end_error", "restart vote")
|
|
else
|
|
world << "<span style='boldannounce'>Notice:Restart vote will not restart the server automatically because there are active admins on.</span>"
|
|
message_admins("A restart vote has passed, but there are active admins on with +server, so it has been canceled. If you wish, you may restart the server.")
|
|
|
|
return .
|
|
|
|
/datum/subsystem/vote/proc/submit_vote(vote)
|
|
if(mode)
|
|
if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
|
|
return 0
|
|
if(!(usr.ckey in voted))
|
|
if(vote && 1<=vote && vote<=choices.len)
|
|
voted += usr.ckey
|
|
choices[choices[vote]]++ //check this
|
|
return vote
|
|
return 0
|
|
|
|
/datum/subsystem/vote/proc/initiate_vote(vote_type, initiator_key)
|
|
if(!mode)
|
|
if(started_time)
|
|
var/next_allowed_time = (started_time + config.vote_delay)
|
|
if(mode)
|
|
usr << "<span class='warning'>There is already a vote in progress! please wait for it to finish.</span>"
|
|
return 0
|
|
|
|
var/admin = FALSE
|
|
var/ckey = ckey(initiator_key)
|
|
if((admin_datums[ckey]) || (ckey in deadmins))
|
|
admin = TRUE
|
|
|
|
if(next_allowed_time > world.time && !admin)
|
|
usr << "<span class='warning'>A vote was initiated recently, you must wait roughly [(next_allowed_time-world.time)/10] seconds before a new vote can be started!</span>"
|
|
return 0
|
|
|
|
reset()
|
|
switch(vote_type)
|
|
if("restart")
|
|
choices.Add("Restart Round","Continue Playing")
|
|
if("gamemode")
|
|
choices.Add(config.votable_modes)
|
|
if("custom")
|
|
question = stripped_input(usr,"What is the vote for?")
|
|
if(!question)
|
|
return 0
|
|
for(var/i=1,i<=10,i++)
|
|
var/option = capitalize(stripped_input(usr,"Please enter an option or hit cancel to finish"))
|
|
if(!option || mode || !usr.client)
|
|
break
|
|
choices.Add(option)
|
|
else
|
|
return 0
|
|
mode = vote_type
|
|
initiator = initiator_key
|
|
started_time = world.time
|
|
var/text = "[capitalize(mode)] vote started by [initiator]."
|
|
if(mode == "custom")
|
|
text += "\n[question]"
|
|
log_vote(text)
|
|
world << "\n<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=\ref[src]'>here</a> to place your votes.\nYou have [config.vote_period/10] seconds to vote.</font>"
|
|
time_remaining = round(config.vote_period/10)
|
|
for(var/c in clients)
|
|
var/client/C = c
|
|
var/datum/action/vote/V = new
|
|
V.Grant(C.mob)
|
|
generated_actions += V
|
|
return 1
|
|
return 0
|
|
|
|
/datum/subsystem/vote/proc/interface(client/C)
|
|
if(!C)
|
|
return
|
|
var/admin = 0
|
|
var/trialmin = 0
|
|
if(C.holder)
|
|
admin = 1
|
|
if(check_rights_for(C, R_ADMIN))
|
|
trialmin = 1
|
|
voting |= C
|
|
|
|
if(mode)
|
|
if(question)
|
|
. += "<h2>Vote: '[question]'</h2>"
|
|
else
|
|
. += "<h2>Vote: [capitalize(mode)]</h2>"
|
|
. += "Time Left: [time_remaining] s<hr><ul>"
|
|
for(var/i=1,i<=choices.len,i++)
|
|
var/votes = choices[choices[i]]
|
|
if(!votes)
|
|
votes = 0
|
|
. += "<li><a href='?src=\ref[src];vote=[i]'>[choices[i]]</a> ([votes] votes)</li>"
|
|
. += "</ul><hr>"
|
|
if(admin)
|
|
. += "(<a href='?src=\ref[src];vote=cancel'>Cancel Vote</a>) "
|
|
else
|
|
. += "<h2>Start a vote:</h2><hr><ul><li>"
|
|
//restart
|
|
if(trialmin || config.allow_vote_restart)
|
|
. += "<a href='?src=\ref[src];vote=restart'>Restart</a>"
|
|
else
|
|
. += "<font color='grey'>Restart (Disallowed)</font>"
|
|
if(trialmin)
|
|
. += "\t(<a href='?src=\ref[src];vote=toggle_restart'>[config.allow_vote_restart?"Allowed":"Disallowed"]</a>)"
|
|
. += "</li><li>"
|
|
//gamemode
|
|
if(trialmin || config.allow_vote_mode)
|
|
. += "<a href='?src=\ref[src];vote=gamemode'>GameMode</a>"
|
|
else
|
|
. += "<font color='grey'>GameMode (Disallowed)</font>"
|
|
if(trialmin)
|
|
. += "\t(<a href='?src=\ref[src];vote=toggle_gamemode'>[config.allow_vote_mode?"Allowed":"Disallowed"]</a>)"
|
|
|
|
. += "</li>"
|
|
//custom
|
|
if(trialmin)
|
|
. += "<li><a href='?src=\ref[src];vote=custom'>Custom</a></li>"
|
|
. += "</ul><hr>"
|
|
. += "<a href='?src=\ref[src];vote=close' style='position:absolute;right:50px'>Close</a>"
|
|
return .
|
|
|
|
|
|
/datum/subsystem/vote/Topic(href,href_list[],hsrc)
|
|
if(!usr || !usr.client)
|
|
return //not necessary but meh...just in-case somebody does something stupid
|
|
switch(href_list["vote"])
|
|
if("close")
|
|
voting -= usr.client
|
|
usr << browse(null, "window=vote")
|
|
return
|
|
if("cancel")
|
|
if(usr.client.holder)
|
|
reset()
|
|
if("toggle_restart")
|
|
if(usr.client.holder)
|
|
config.allow_vote_restart = !config.allow_vote_restart
|
|
if("toggle_gamemode")
|
|
if(usr.client.holder)
|
|
config.allow_vote_mode = !config.allow_vote_mode
|
|
if("restart")
|
|
if(config.allow_vote_restart || usr.client.holder)
|
|
initiate_vote("restart",usr.key)
|
|
if("gamemode")
|
|
if(config.allow_vote_mode || usr.client.holder)
|
|
initiate_vote("gamemode",usr.key)
|
|
if("custom")
|
|
if(usr.client.holder)
|
|
initiate_vote("custom",usr.key)
|
|
else
|
|
submit_vote(round(text2num(href_list["vote"])))
|
|
usr.vote()
|
|
|
|
/datum/subsystem/vote/proc/remove_action_buttons()
|
|
for(var/v in generated_actions)
|
|
var/datum/action/vote/V = v
|
|
if(!qdeleted(V))
|
|
V.Remove(V.owner)
|
|
generated_actions = list()
|
|
|
|
/mob/verb/vote()
|
|
set category = "OOC"
|
|
set name = "Vote"
|
|
|
|
var/datum/browser/popup = new(src, "vote", "Voting Panel")
|
|
popup.set_window_options("can_close=0")
|
|
popup.set_content(SSvote.interface(client))
|
|
popup.open(0)
|
|
|
|
/datum/action/vote
|
|
name = "Vote!"
|
|
button_icon_state = "vote"
|
|
|
|
/datum/action/vote/Trigger()
|
|
if(owner)
|
|
owner.vote()
|
|
Remove(owner)
|
|
|
|
/datum/action/vote/IsAvailable()
|
|
return 1 |