From fb8341f6aeb7258a414831778728641fddb17d6a Mon Sep 17 00:00:00 2001 From: Atlantis Date: Sat, 28 Nov 2015 07:31:23 +0100 Subject: [PATCH] Adds NTNRC (NTNet Relay Chat) - Adds NTNet relay chat, communication method that works as alternative to PDA messaging. - NTNRC client is available to everyone for download. Once started, you can set your username (much as you can with IRC in real) and join channels created by other users (or create your own channel) - Each channel has an operator. This is typically the channel's creator. If the operator leaves, operator status is transferred to randomly picked other user. If all users leave operator status is set as null until someone arrives again. - Operator can delete or rename the channel, as well as save copy of chat logs to the hard drive (in future it will be editable/printable) and set channel password. Password-locked channels are private only for those who know the password. - Added new access type (ID 42): access_network. RD is now, by default, given this access. - People with network access can enter administrative mode, which allows them to make operator actions even without operator status on all channels. Furthermore, this hides them from other users (unless they decide to talk). They also bypass password locks, if they are set. - Minor tweaks to NTNet monitoring tool. Fixed one minor graphical glitch in the UI and added access requirement to run the program (network access) - Screenshot of NTNRC in action: http://i.imgur.com/c7hrWY5.png --- baystation12.dme | 3 + code/game/jobs/access_datum.dm | 7 +- code/game/jobs/job/science.dm | 4 +- .../NTNet/NTNRC/conversation.dm | 63 ++++++ code/modules/modular_computers/NTNet/NTNet.dm | 3 +- .../modular_computers/file_system/data.dm | 10 +- .../modular_computers/file_system/program.dm | 10 +- .../file_system/programs/_engineering.dm | 12 +- .../file_system/programs/_medical.dm | 9 + .../file_system/programs/ntmonitor.dm | 1 + .../file_system/programs/ntnrc_client.dm | 199 ++++++++++++++++++ nano/templates/ntnet_chat.tmpl | 66 ++++++ nano/templates/ntnet_monitor.tmpl | 2 +- 13 files changed, 369 insertions(+), 20 deletions(-) create mode 100644 code/modules/modular_computers/NTNet/NTNRC/conversation.dm create mode 100644 code/modules/modular_computers/file_system/programs/_medical.dm create mode 100644 code/modules/modular_computers/file_system/programs/ntnrc_client.dm create mode 100644 nano/templates/ntnet_chat.tmpl diff --git a/baystation12.dme b/baystation12.dme index bc41f20e046..96c57d08827 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -1390,9 +1390,11 @@ #include "code\modules\modular_computers\file_system\data.dm" #include "code\modules\modular_computers\file_system\program.dm" #include "code\modules\modular_computers\file_system\programs\_engineering.dm" +#include "code\modules\modular_computers\file_system\programs\_medical.dm" #include "code\modules\modular_computers\file_system\programs\configurator.dm" #include "code\modules\modular_computers\file_system\programs\ntdownloader.dm" #include "code\modules\modular_computers\file_system\programs\ntmonitor.dm" +#include "code\modules\modular_computers\file_system\programs\ntnrc_client.dm" #include "code\modules\modular_computers\hardware\card_slot.dm" #include "code\modules\modular_computers\hardware\hard_drive.dm" #include "code\modules\modular_computers\hardware\hardware.dm" @@ -1401,6 +1403,7 @@ #include "code\modules\modular_computers\hardware\tesla_link.dm" #include "code\modules\modular_computers\NTNet\NTNet.dm" #include "code\modules\modular_computers\NTNet\NTNet_relay.dm" +#include "code\modules\modular_computers\NTNet\NTNRC\conversation.dm" #include "code\modules\nano\_JSON.dm" #include "code\modules\nano\JSON Reader.dm" #include "code\modules\nano\JSON Writer.dm" diff --git a/code/game/jobs/access_datum.dm b/code/game/jobs/access_datum.dm index 5e1cb90f1f2..9ccdf195da3 100644 --- a/code/game/jobs/access_datum.dm +++ b/code/game/jobs/access_datum.dm @@ -256,7 +256,12 @@ desc = "Quartermaster" region = ACCESS_REGION_SUPPLY -// /var/const/free_access_id = 43 +/var/const/access_network = 42 +/datum/access/network + id = access_network + desc = "Station Network" + region = ACCESS_REGION_RESEARCH + // /var/const/free_access_id = 43 // /var/const/free_access_id = 44 diff --git a/code/game/jobs/job/science.dm b/code/game/jobs/job/science.dm index 05690d43390..f1fe2ffe08b 100644 --- a/code/game/jobs/job/science.dm +++ b/code/game/jobs/job/science.dm @@ -15,11 +15,11 @@ access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, access_tox_storage, access_teleporter, access_sec_doors, access_research, access_robotics, access_xenobiology, access_ai_upload, access_tech_storage, - access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch) + access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_network) minimal_access = list(access_rd, access_heads, access_tox, access_genetics, access_morgue, access_tox_storage, access_teleporter, access_sec_doors, access_research, access_robotics, access_xenobiology, access_ai_upload, access_tech_storage, - access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch) + access_RC_announce, access_keycard_auth, access_tcomsat, access_gateway, access_xenoarch, access_network) minimal_player_age = 14 equip(var/mob/living/carbon/human/H) diff --git a/code/modules/modular_computers/NTNet/NTNRC/conversation.dm b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm new file mode 100644 index 00000000000..d1f390af3ba --- /dev/null +++ b/code/modules/modular_computers/NTNet/NTNRC/conversation.dm @@ -0,0 +1,63 @@ +/datum/ntnet_conversation/ + var/title = "Untitled Conversation" + var/datum/computer_file/program/chatclient/operator // "Administrator" of this channel. Creator starts as channel's operator, + var/list/messages = list() + var/list/clients = list() + var/password + +/datum/ntnet_conversation/New() + if(ntnet_global) + ntnet_global.chat_channels.Add(src) + ..() + +/datum/ntnet_conversation/proc/add_message(var/message, var/username) + message = "[worldtime2text()] [username]: [message]" + messages.Add(message) + trim_message_list() + +/datum/ntnet_conversation/proc/add_status_message(var/message) + messages.Add("-!- [message]") + trim_message_list() + +/datum/ntnet_conversation/proc/trim_message_list() + if(messages.len <= 50) + return + for(var/message in messages) + messages -= message + if(messages <= 50) + return + +/datum/ntnet_conversation/proc/add_client(var/datum/computer_file/program/chatclient/C) + if(!istype(C)) + return + clients.Add(C) + add_status_message("[C.username] has joined the channel.") + // No operator, so we assume the channel was empty. Assign this user as operator. + if(!operator) + changeop(C) + +/datum/ntnet_conversation/proc/remove_client(var/datum/computer_file/program/chatclient/C) + if(!istype(C) || !(C in clients)) + return + clients.Remove(C) + add_status_message("[C.username] has left the channel.") + + // Channel operator left, pick new operator + if(C == operator) + operator = null + if(clients.len) + var/datum/computer_file/program/chatclient/newop = pick(clients) + changeop(newop) + + +/datum/ntnet_conversation/proc/changeop(var/datum/computer_file/program/chatclient/newop) + if(istype(newop)) + operator = newop + add_status_message("Channel operator status transferred to [newop.username].") + +/datum/ntnet_conversation/proc/change_title(var/newtitle, var/datum/computer_file/program/chatclient/client) + if(operator != client) + return 0 // Not Authorised + + add_status_message("[client.username] has changed channel title from [title] to [newtitle]") + title = newtitle \ No newline at end of file diff --git a/code/modules/modular_computers/NTNet/NTNet.dm b/code/modules/modular_computers/NTNet/NTNet.dm index 8157fbd287a..6fe318e856c 100644 --- a/code/modules/modular_computers/NTNet/NTNet.dm +++ b/code/modules/modular_computers/NTNet/NTNet.dm @@ -7,6 +7,7 @@ var/global/datum/ntnet/ntnet_global = new() var/list/logs = list() var/list/available_station_software = list() var/list/available_antag_software = list() + var/list/chat_channels = list() // Amount of logs the system tries to keep in memory. Keep below 999 to prevent byond from acting weirdly. // High values make displaying logs much laggier. @@ -39,7 +40,7 @@ var/global/datum/ntnet/ntnet_global = new() if(source) log_text += "[source.get_network_tag()] - " else - log_text += " *SYSTEM* - " + log_text += "*SYSTEM* - " log_text += log_string logs.Add(log_text) diff --git a/code/modules/modular_computers/file_system/data.dm b/code/modules/modular_computers/file_system/data.dm index fad70ab013b..4e4b8a1f8db 100644 --- a/code/modules/modular_computers/file_system/data.dm +++ b/code/modules/modular_computers/file_system/data.dm @@ -7,4 +7,12 @@ /datum/computer_file/data/clone() var/datum/computer_file/data/temp = ..() temp.stored_data = stored_data - return temp \ No newline at end of file + return temp + +// Calculates file size from amount of characters in saved string +/datum/computer_file/data/proc/calculate_size(var/block_size = 250) + size = max(1, round(length(stored_data) / block_size)) + +/datum/computer_file/data/logfile + filetype = "LOG" + diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index b6697db96d9..f3fb07d618b 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -54,8 +54,12 @@ // Check if the user can run program. Only humans can operate computer. Automatically called in run_program() // User has to wear their ID or have it inhand for ID Scan to work. -/datum/computer_file/program/proc/can_run(var/mob/living/user, var/loud = 0) - if(!required_access) // No required_access, allow it. +// Can also be called manually, with optional parameter being access_to_check to scan the user's ID +/datum/computer_file/program/proc/can_run(var/mob/living/user, var/loud = 0, var/access_to_check) + // Defaults to required_access + if(!access_to_check) + access_to_check = required_access + if(!access_to_check) // No required_access, allow it. return 1 if(istype(user, /mob/living/silicon)) // AI or robot. Allow it. return 1 @@ -75,7 +79,7 @@ user << "\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning." return 0 - if(required_access in I.access) + if(access_to_check in I.access) return 1 if(loud && computer) user << "\The [computer] flashes an \"Access Denied\" warning." diff --git a/code/modules/modular_computers/file_system/programs/_engineering.dm b/code/modules/modular_computers/file_system/programs/_engineering.dm index 46bee56bd6a..5b71325825a 100644 --- a/code/modules/modular_computers/file_system/programs/_engineering.dm +++ b/code/modules/modular_computers/file_system/programs/_engineering.dm @@ -44,14 +44,4 @@ network_destination = "RCON remote control system" requires_ntnet_feature = NTNET_SYSTEMCONTROL usage_flags = PROGRAM_LAPTOP | PROGRAM_CONSOLE - size = 25 - -/datum/computer_file/program/suit_sensors - filename = "sensormonitor" - filedesc = "Suit Sensors Monitoring" - nanomodule_path = /datum/nano_module/crew_monitor - program_icon_state = "crew" - keyboard_icon_state = "keyboard7" - requires_ntnet = 1 - network_destination = "crew lifesigns monitoring system" - size = 20 \ No newline at end of file + size = 25 \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/_medical.dm b/code/modules/modular_computers/file_system/programs/_medical.dm new file mode 100644 index 00000000000..eec28630cd3 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/_medical.dm @@ -0,0 +1,9 @@ +/datum/computer_file/program/suit_sensors + filename = "sensormonitor" + filedesc = "Suit Sensors Monitoring" + nanomodule_path = /datum/nano_module/crew_monitor + program_icon_state = "crew" + keyboard_icon_state = "keyboard7" + requires_ntnet = 1 + network_destination = "crew lifesigns monitoring system" + size = 20 \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/ntmonitor.dm index 81cb1a6d2ad..90f7e6b645c 100644 --- a/code/modules/modular_computers/file_system/programs/ntmonitor.dm +++ b/code/modules/modular_computers/file_system/programs/ntmonitor.dm @@ -4,6 +4,7 @@ program_icon_state = "generic" size = 7 requires_ntnet = 1 + required_access = access_network available_on_ntnet = 1 nanomodule_path = /datum/nano_module/computer_ntnetmonitor/ diff --git a/code/modules/modular_computers/file_system/programs/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm new file mode 100644 index 00000000000..fac7a3d36b3 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/ntnrc_client.dm @@ -0,0 +1,199 @@ +/datum/computer_file/program/chatclient + filename = "ntnrc_client" + filedesc = "NTNet Relay Chat Client" + program_icon_state = "generic" + size = 10 + requires_ntnet = 1 + requires_ntnet_feature = NTNET_COMMUNICATION + network_destination = "NTNRC server" + available_on_ntnet = 1 + nanomodule_path = /datum/nano_module/computer_chatclient/ + var/username + var/datum/ntnet_conversation/channel = null + var/operator_mode = 0 // Channel operator mode + var/netadmin_mode = 0 // Administrator mode (invisible to other users + bypasses passwords) + +/datum/computer_file/program/chatclient/New() + username = "DefaultUser[rand(100, 999)]" + +/datum/computer_file/program/chatclient/Topic(href, href_list) + if(href_list["PRG_speak"]) + if(!channel) + return + var/mob/living/user = usr + var/message = sanitize(input(user, "Enter message or leave blank to cancel: ")) + if(!message || !channel) + return + channel.add_message(message, username) + + if(href_list["PRG_joinchannel"]) + var/datum/ntnet_conversation/C + for(var/datum/ntnet_conversation/chan in ntnet_global.chat_channels) + if(chan.title == href_list["PRG_joinchannel"]) + C = chan + break + + if(!C) + return + + if(netadmin_mode) + channel = C // Bypasses normal leave/join and passwords. Technically makes the user invisible to others. + return + + if(C.password) + var/mob/living/user = usr + var/password = sanitize(input(user,"Access Denied. Enter password:")) + if(C && (password == C.password)) + C.add_client(src) + channel = C + return + C.add_client(src) + channel = C + if(href_list["PRG_leavechannel"]) + if(channel) + channel.remove_client(src) + channel = null + if(href_list["PRG_newchannel"]) + var/mob/living/user = usr + var/channel_title = sanitize(input(user,"Enter channel name or leave blank to cancel:")) + if(!channel_title) + return + var/datum/ntnet_conversation/C = new/datum/ntnet_conversation() + C.add_client(src) + C.operator = src + channel = C + C.title = channel_title + if(href_list["PRG_toggleadmin"]) + if(netadmin_mode) + netadmin_mode = 0 + if(channel) + channel.remove_client(src) // We shouldn't be in channel's user list, but just in case... + channel = null + return + var/mob/living/user = usr + if(can_run(usr, 1, access_network)) + if(channel) + var/response = alert(user, "Really engage admin-mode? You will be disconnected from your current channel!", "NTNRC Admin mode", "Yes", "No") + if(response == "Yes") + if(channel) + channel.remove_client(src) + channel = null + else + return + netadmin_mode = 1 + if(href_list["PRG_changename"]) + var/mob/living/user = usr + var/newname = sanitize(input(user,"Enter new nickname or leave blank to cancel:")) + if(!newname) + return + if(channel) + channel.add_status_message("[username] is now known as [newname].") + username = newname + + if(href_list["PRG_savelog"]) + if(!channel) + return + var/mob/living/user = usr + var/logname = input(user,"Enter desired logfile name (.log) or leave blank to cancel:") + if(!logname || !channel) + return + var/datum/computer_file/data/logfile = new/datum/computer_file/data/logfile() + // Now we will generate HTML-compliant file that can actually be viewed/printed. + logfile.stored_data = "Logfile dump from NTNRC channel [channel.title]
" + for(var/logstring in channel.messages) + logfile.stored_data += "[logstring]
" + logfile.stored_data = "Logfile dump completed." + logfile.calculate_size() + if(!computer || !computer.hard_drive || !computer.hard_drive.store_file(logfile)) + if(!computer) + // This program shouldn't even be runnable without computer. + CRASH("Var computer is null!") + return + if(!computer.hard_drive) + computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive connection error\" warning.") + else // In 99.9% cases this will mean our HDD is full + computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive may be full. Please free some space and try again. Required space: [logfile.size]GQ\" warning.") + if(href_list["PRG_renamechannel"]) + if(!operator_mode || !channel) + return + var/mob/living/user = usr + var/newname = sanitize(input(user, "Enter new channel name or leave blank to cancel:")) + if(!newname || !channel) + return + channel.add_status_message("Channel renamed from [channel.title] to [newname] by operator.") + channel.title = newname + if(href_list["PRG_deletechannel"]) + if(channel && ((channel.operator == src) || netadmin_mode)) + del(channel) + if(href_list["PRG_setpassword"]) + if(!channel || ((channel.operator != src) && !netadmin_mode)) + return + + var/mob/living/user = usr + var/newpassword = sanitize(input(user, "Enter new password for this channel. Leave blank to cancel, enter 'nopassword' to remove password completely:")) + if(!channel || !newpassword || ((channel.operator != src) && !netadmin_mode)) + return + + if(newpassword == "nopassword") + channel.password = "" + else + channel.password = newpassword + + ..(href, href_list) + + +/datum/computer_file/program/chatclient/kill_program(var/forced = 0) + if(channel) + channel.remove_client(src) + channel = null + ..(forced) + +/datum/nano_module/computer_chatclient + name = "NTNet Relay Chat Client" + +/datum/nano_module/computer_chatclient/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) + if(!ntnet_global || !ntnet_global.chat_channels) + return + + var/list/data = list() + if(program) + data = program.get_header_data() + + var/datum/computer_file/program/chatclient/C = program + if(!istype(C)) + return + + data["adminmode"] = C.netadmin_mode + if(C.channel) + data["title"] = C.channel.title + var/list/messages[0] + for(var/M in C.channel.messages) + messages.Add(list(list( + "msg" = M + ))) + data["messages"] = messages + var/list/clients[0] + for(var/datum/computer_file/program/chatclient/cl in C.channel.clients) + clients.Add(list(list( + "name" = cl.username + ))) + data["clients"] = clients + C.operator_mode = (C.channel.operator == C) ? 1 : 0 + data["is_operator"] = C.operator_mode || C.netadmin_mode + + else // Channel selection screen + var/list/all_channels[0] + for(var/datum/ntnet_conversation/conv in ntnet_global.chat_channels) + if(conv && conv.title) + all_channels.Add(list(list( + "chan" = conv.title + ))) + data["all_channels"] = all_channels + + ui = nanomanager.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "ntnet_chat.tmpl", "NTNet Relay Chat Client", 575, 700, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + ui.set_auto_update(1) \ No newline at end of file diff --git a/nano/templates/ntnet_chat.tmpl b/nano/templates/ntnet_chat.tmpl new file mode 100644 index 00000000000..6424e073e25 --- /dev/null +++ b/nano/templates/ntnet_chat.tmpl @@ -0,0 +1,66 @@ +{{if data.adminmode}} +

ADMINISTRATIVE MODE

+{{/if}} + +{{if data.title}} +
+ Current channel: +
+
+ {{:data.title}} +
+
+ Operator access: +
+
+ {{if data.is_operator}} + Enabled + {{else}} + Disabled + {{/if}} +
+
+ Controls: +
+
+ +
{{:helper.link("Send message", null, {'PRG_speak' : 1})}} +
{{:helper.link("Change nickname", null, {'PRG_changename' : 1})}} +
{{:helper.link("Toggle administration mode", null, {'PRG_toggleadmin' : 1})}} +
{{:helper.link("Leave channel", null, {'PRG_leavechannel' : 1})}} +
{{:helper.link("Save log to local drive", null, {'PRG_savelog' : 1})}} + {{if data.is_operator}} +
{{:helper.link("Rename channel", null, {'PRG_renamechannel' : 1})}} +
{{:helper.link("Set password", null, {'PRG_setpassword' : 1})}} +
{{:helper.link("Delete channel", null, {'PRG_deletechannel' : 1})}} + {{/if}} +
+
+ Chat Window +
+
+
+ {{for data.messages}} + {{:value.msg}}
+ {{/for}} +
+
+
+ Connected Users
+ {{for data.clients}} + {{:value.name}}
+ {{/for}} +{{else}} + Controls: + +
{{:helper.link("Change nickname", null, {'PRG_changename' : 1})}} +
{{:helper.link("New Channel", null, {'PRG_newchannel' : 1})}} +
{{:helper.link("Toggle Administration Mode", null, {'PRG_toggleadmin' : 1})}} +
+ Available channels: + + {{for data.all_channels}} +
{{:helper.link(value.chan, null, {'PRG_joinchannel' : value.chan})}}
+ {{/for}} +
+{{/if}} \ No newline at end of file diff --git a/nano/templates/ntnet_monitor.tmpl b/nano/templates/ntnet_monitor.tmpl index 13245bd8ae9..e200eb83823 100644 --- a/nano/templates/ntnet_monitor.tmpl +++ b/nano/templates/ntnet_monitor.tmpl @@ -21,7 +21,7 @@

Caution - Disabling wireless transmitters when using wireless device may prevent you from re-enabling them again! {{else}} -

Wireless coverage unavailable, no relays are connected.

+

Wireless coverage unavailable, no relays are connected.

{{/if}}