@program radio.muf 1 9999 d i ( radio.muf version 1.0 by Wog Based slightly on SPR's communicator system. Emulates a radio network. Different channels can be listened, to and transmitted on. Channels can be numbers, names or whatever. They are case-insensitive. SETUP - Attach an action to this program from a room or thing. Then type alone... PROPS - /on this program:/ _radio/: This stores ACTIONS to this program. The props. are gotten from the action not their location. _radio_lock/: /on actions:/ _channel: .key: _on?: _hidename?: _callsign: ~snooper?:yes -- let's wizzies snoop in on real names/decode everything.. _format:Message format... _recieve_only?:yes _radio_listen:program to listen with... * /on using players:/ ~/radioban?:yes CALLING - This proggie can be called from Mucker3 or above proggies, by $include-ing this program, assuming a wizzie has run this proggie with the #psetup argument. The methods they can use are: do-radio-message { message callsign name type channel -- } Sends out a message for 'name' of type 'type'. All props for things like says will be retrived from me @. If you want to make it an anonymous say, it is reccomended you set that to the dbref of your proggie or something similar, since #-1 WILL crash the proggie. do-radio-message-for { message type encoded? radiodbref -- } Same as above, but gets the callsign prop, and whether to hide names from view, etc. for the radio action radiodbref. do-radio-message-encrypted { message callsign name type key channel -- } Same as do-radio-message, but encrypts message with key key. do-radio-getlock { channel -- lock 1 OR 0 } Returns the radio lock for that channel and the number 1, or 0 and nothing. Requires only a Mucker2 bit to call. Values for TYPE: MESG_TYPE_NONE <-- find type based on message data. As in pose if it starts with a ':'. MESG_TYPE_SAY <-- do a say. MESG_TYPE_POSE <-- always pose. MESG_TYPE_SPOOF <-- spoof it MESG_TYPE_FSPOOF <-- do a freespoof. {As in no parens around the message.} * This is like the _listen props, but the format the M3 or above program is called with is slightly different. However, some _listen-ing proggies will work, and if not they will require minimal modification. The program's MAIN function will recieve the following: trigger @ => the radio listener object. command @ => "(_radio_listen}" [That's a paren, but they can't be in comments.] me @ => The me @ this program has, from "me" match or the M3 proggie that called it. Otherwise the stack is like this: channel callsign 0 message if the message was not encrypted. but if the message was encrypted and the object the _radio_listen is on has the proper key: channel callsign non-encrypted-message key 1 1 message but if the object does not have the proper key: channel callsign encrypted-message-text key 0 1 message Support for the _radio_listen prop can be disabled below by @ $def. --- Change History ---------------------------------- v 1.0 Mar 12 2000 First version. v 1.1 Apr 07 2000 Added _radio_listen support. v 1.11 May 27 2000 Minor bugfix to lock-setting code. --- Distrubution Information ------------------------ Copyright {C} Charles "Wog" Reiss This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or {at your option} any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. For a copy of the GPL: a> see: http://www.gnu.org/copyleft/gpl.html b> write to: the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA ) (Change this to $undef if you want channel names to be only numbers.) $def CH_STRINGS $def DEFAULT_FORMAT "[ CH %channel -- %callsign ] %message" (Change this to $undef if you do not want to allow _radio_listen prop support.) $def RADIO_LISTENERS $ifdef CH_STRINGS $def .ch_intostr ( do nothing ) $def .ch_atoi ( also do nothing ) $def .ch_eq stringcmp not $else $def .ch_intostr intostr $def .ch_atoi atoi $def .ch_eq = $endif (My version of the .yes? macro. I like this one...) $def .yes? 1 strcut pop tolower "y" strcmp not $undef DO_DEBUG $include $lib/reflist $include $lib/case ( This is one of: - none {fiquire out based on context} - say - pose - spoof - freespoof ) lvar mesg_type $def MESG_TYPE_NONE 0 $def MESG_TYPE_SAY 1 $def MESG_TYPE_POSE 2 $def MESG_TYPE_SPOOF 3 $def MESG_TYPE_FSPOOF 4 lvar mesg lvar sec_mesg (encrypted, maybe?) lvar encoded? lvar mesg_name lvar callsign lvar channel lvar key lvar list lvar tmp : getMessage ( d -- s ) dup "_format" getpropstr dup not if pop DEFAULT_FORMAT then callsign @ "%callsign" subst channel @ .ch_intostr "%channel" subst encoded? @ if over ".key" getpropstr key @ strcmp not 3 pick "~snooper?" getpropstr .yes? or if sec_mesg @ else mesg @ then else mesg @ then "%message" subst swap "~snooper?" getpropstr .yes? if me @ unparseobj else "" then "%rname" subst ; $ifdef RADIO_LISTENERS (Handle all _radio_listen stuff here.) : getMLevel ( d -- i ) $ifdef __glowver mlevel $else $ifdef __neon mlevel $else dup "W" flag? if pop 4 else mlevel then $endif $endif ; : validListener? ( rdb d1 -- b ) dup program? not if pop 0 exit then dup getMLevel 3 < if pop 0 exit then swap owner over controls swap "L" flag? or ; : do-listeners ( d -- ) dup "_radio_listen" getprop dup int? if dup 0 = if pop exit then then dup string? if dup "#" stringpfx if 1 strcut swap pop then atoi dbref else dup int? if atoi dbref else dup dbref? not if pop pop exit then then then ( We should have a dbref now.) over over validListener? not if pop pop exit then ( Good! We have a good program! Call it now! ) fork dup -1 = if "Couldn't fork!" abort then not if ( Child. ) "(_radio_listen)" command ! tmp ! trigger ! begin pop depth 0 = until (Kill the stack, except for two items.) channel @ callsign @ encoded? @ if trigger @ ".key" getpropstr key @ strcmp not trigger @ "~snooper?" getpropstr .yes? or if mesg @ key @ 1 1 sec_mesg @ else sec_mesg @ key @ 0 1 mesg @ then else 0 mesg @ then tmp @ call pid kill else pop pop then ; $else $def do-listeners ( ** DO NOTHING ** ) $endif : send "_radio/" channel @ .ch_intostr strcat list ! prog list @ REF-first begin dup #-1 dbcmp not while (Make sure it is a valid action...) dup exit? not if 1 else (This is a very paranoid check of list integrity. Makes sure everything is in good order.) dup getlink prog dbcmp not over "_on?" getpropstr .yes? not or over "_channel" getpropstr .ch_atoi channel @ .ch_eq not or if 1 else 0 then then if $ifdef DO_DEBUG "INVALID ACTION ON REFLIST! REMOVING!" .tell $endif (Not linked up right! Remove from list!) dup prog list @ rot REF-next swap prog list @ rot REF-delete continue then dup do-listeners dup "_silent?" getpropstr .yes? not if dup getMessage over location dup thing? if location then #-1 rot notify_except then prog list @ rot REF-next repeat pop ; : removeReceiver "_radio/" channel @ .ch_intostr strcat list ! prog list @ trigger @ REF-delete ; : addReceiver "_radio/" channel @ .ch_intostr strcat list ! prog list @ trigger @ REF-add ; : charSub ( s -- s' ) "sl" "/" subst "co" ":" subst "at" "@" subst "tw" "~" subst ; : findSayPropdir ( s -- s i ) (Returnsnot string if none) ( i = 1 if there is some special setting for first char. ) (Takes first char of say and finds the propdir to get props from.) charSub "_say/" swap strcat dup me @ swap propdir? not if pop me @ "_say/def" propdir? if "_say/def/" 0 else "" 0 then else "/" strcat 1 then ; : mePropDefault ( prop default -- string ) swap me @ swap getpropstr dup if swap pop else pop then ; : takeCareOfComma ( s -- s' ) (Adds comma to end of string.) dup dup strlen 1 - strcut swap pop "," strcmp (as in doesn't match) if "," strcat then ; : sayize ( s -- osay ) (Returns 'name says, "...."' string ) dup 1 strcut pop findSayPropdir if (Special setting....?) swap 1 strcut swap pop swap then dup if dup "quotes" strcat "\"%m\"" mePropDefault else "\"%m\"" then rot "%m" subst ( propdir quoted ) over if swap "osay" strcat "says" mePropDefault else "says" then takeCareOfComma " " strcat mesg_name @ " " strcat swap strcat swap strcat ; : posize ( s -- s' ) "':!-? .,;" over 1 strcut pop instr not if " " swap strcat then mesg_name @ swap strcat ; : spoofize ( s -- s' ) "( " swap strcat " )" strcat ; : findMesgType ( s -- s' ) (Might modify string...) mesg_type @ MESG_TYPE_NONE = if dup 1 strcut swap ":" strcmp not if swap pop MESG_TYPE_POSE mesg_type ! else pop MESG_TYPE_SAY mesg_type ! then then ; : findMessage ( s -- s' ) findMesgType mesg_type @ case MESG_TYPE_SAY = when sayize end MESG_TYPE_POSE = when posize end MESG_TYPE_SPOOF = when spoofize end MESG_TYPE_FSPOOF = when end default "Unrecognized Message Type!" abort end endcase mesg ! ; : checkLocks prog "_radio_lock/" channel @ .ch_intostr strcat getprop dup if trigger @ over testlock me @ swap testlock or (If me @ or trigger @ match okie...) else not then (Yeah, I know, the above brillance is a very cheap way of making the program one instruction shorter, and harder to read. Ain't it special?) ; : setlock ( s -- ) ( string of form CHANNEL=LOCK or CHANNEL= to unlock. ) me @ "W" flag? not if pop ">> Only wizzies can do that!" .tell exit then dup "=" instr not if pop ">> SetLock SYNTAX: CHANNEL=LOCK or CHANNEL= to unset lock." .tell exit then strip "=" .split $ifndef CH_STRINGS swap dup number? not if pop pop ">> Channel should be a number!" .tell exit then $endif "_radio_lock/" swap strcat swap dup not if pop prog swap remove_prop ">> Lock cleared." .tell exit then parselock dup not if ">> Invalid lockstring." .tell pop pop exit then prog -3 rotate setprop ">> Lock set." .tell ; : findName trigger @ "_hidename?" getpropstr .yes? if "??" mesg_name ! else me @ name mesg_name ! then ; : findCallsign trigger @ "_callsign" getpropstr dup not if pop "??" then callsign ! ; $def ENC_CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 " ( 1 2 3 4 5) (12345678901234567890123456789012345678901234567890) $def ENC_CHAR_LEN 44 (This makes approx. 1/5 of the fake encrypted data be spaces....) : doEncrypt ( This will use the value of mesg @ to set sec_mesg @ and generate mesg @ ) mesg @ strlen mesg @ "(Encrypted) " swap strcat sec_mesg ! "(Encoded Data) " mesg ! begin dup while random ENC_CHAR_LEN % 1 + dup 1 + ENC_CHARS swap strcut pop swap strcut swap pop mesg @ swap strcat mesg ! 1 - repeat pop ; : doMessage ( s -- ) checkLocks not if ">> You can't transmit on that frequency!" .tell exit then findName findCallsign findMessage encoded? @ if doEncrypt then send ; : do-on trigger @ "_on?" getpropstr .yes? if ">> But it's already on!" .tell exit then trigger @ "_on?" "yes" setprop addReceiver ">> Now it's on." .tell ; : do-off trigger @ "_on?" getpropstr .yes? not if ">> But it's already off!" .tell exit then trigger @ "_on?" remove_prop removeReceiver ">> Now it's off." .tell ; : checkOn ( -- i ) trigger @ "_on?" getpropstr .yes? not if ">> Must turn the thing on first!" .tell 1 exit then 0 ; : setcallsign trigger @ "_callsign" rot setprop ">> Callsign set." .tell ; : hidenames checkOn if exit then trigger @ "_hidename?" getpropstr .yes? if ">> Names are already hidden!" .tell exit then trigger @ "_hidename?" "yes" setprop ">> Names will now be hidden." .tell ; : shownames checkOn if exit then trigger @ "_hidename?" getpropstr .yes? not if ">> Names are already shown!" .tell exit then trigger @ "_hidename?" remove_prop ">> Names will now be hidden." .tell ; : setformat trigger @ "_format" rot setprop ">> Format set." .tell ; : setkey checkOn if exit then dup trigger @ ".key" rot setprop if ">> Key set." .tell else ">> Key cleared." .tell then ; : cmdSub command @ "CMD" subst ; : doHelp "--{ Radio/Communicator Help }-------------------------------" .tell "This device set to channel " channel @ .ch_intostr strcat .tell "This device is " trigger @ "_on?" getpropstr .yes? not if "NOT " strcat then "on." strcat .tell trigger @ "_silent?" getpropstr .yes? if "This transmitter is set _silent?:yes!" .tell then "-- Options: " .tell " CMD #help -- Yer lookin' at it.\r" " CMD #on -- Turn it on.\r" strcat " CMD #off -- Turn it off.\r" strcat " CMD #key -- Set encryption/decryption key.\r" strcat " CMD #channel -- Sets the channel recieved to \r" strcat " CMD #format -- Set the format for received transmissions:\r" strcat cmdSub .tell trigger @ "_receive_only?" getpropstr .yes? if "This device can NOT transmit." .tell else " CMD #callsign -- Sets the callsign for trasmissions to .\r" " CMD #showname -- Show names on transmission...\r" strcat " CMD #hidename -- Don't show names on transmission...\r" strcat cmdSub .tell "-- Ways to transmit:" .tell " CMD -- transmits as a say.\r" " CMD : -- transmits as a pose.\r" strcat " CMD #say -- transmits as a say.\r" strcat " CMD #pose -- transmits as a pose.\r" strcat " CMD #spoof -- transmits as a spoof.\r" strcat " CMD #fspoof -- transmits as a freespoof, if you are authorized.\r" strcat " CMD ## -- like CMD , but will prefix output with #.\r" strcat " CMD #en -- sends encrypted, with checked on the above.\r" strcat cmdSub .tell then "-- On channels:" .tell $ifdef CH_STRINGS "Channel names may be strings, or numbers of any length." .tell "The names are case-insensitive." .tell $else "Channels are accessed by their ID numbers which must be between" .tell "-2147483648 and 2147483647, inclusive, or maybe different numbers" .tell "depending on what type of machine the MUCK is running on and how" .tell "the MUCK was compiled." .tell $endif "-- A note on formats:" .tell "Formats are the template upon recieved transmissions display will be based. The formats" .tell "are shown when a transmission is recieved after the following substitutions." .tell " %channel is replaced with the number channel." .tell " %message is replaced with actaul message sent." .tell " %callsign is replaced with the callsign of the transmitter." .tell "Formats need not contain all substitutions. Example formats:" .tell " - \"On the radio, %message\"" .tell " - \"[Channel %channel %callsign] %message\"" .tell me @ "W" flag? if "-- Wizard only commands:" .tell " CMD #lock = -- locks channel to key .\r" " CMD #lock = -- unlocks channel ." strcat cmdSub .tell "Locks will be tested on transmitter and the person transmitting." .tell then me @ mlevel 2 > me @ "W" flag? or if "-- Programmers information:" .tell "This program can be called from your programs. @list the first" .tell "few lines for info on this. (Program dbref #" prog int intostr strcat ")" strcat .tell then $ifdef RADIO_LISTENERS "-- _Radio_Listen'ers info." .tell "Programs can be written to listen in on the radio channel by setting" .tell "the _radio_listen prop on a `radio' device that's on, with:" .tell " @propset =dbref:_radio_listen:#dbref" .tell "Programs will typically need to be designed to act in this fasion." .tell "If you are using radio listeners, and don't want to here what it" .tell "recieves than set the _silent?:yes prop on the transmitter." .tell $endif "-- Done. ---------------------------------------------------" .tell ; : setChannel checkOn if exit then strip $ifndef CH_STRINGS dup number? not if ">> A number please!" .tell exit then $endif .ch_atoi removeReceiver dup channel ! trigger @ "_channel" rot .ch_intostr setprop addReceiver ; (--- BEGIN PUBLIC INTERFACES ---) : pub-do-message ( message callsign name type channel -- ) caller mlevel 3 < if "Mucker Level 3 Required." abort then channel ! mesg_type ! mesg_name ! callsign ! findMessage send ; PUBLIC pub-do-message : pub-do-message-encrypt ( message callsign name type key channel -- ) caller mlevel 3 < if "Mucker Level 3 Required." abort then 1 encoded? ! channel ! key ! mesg_type ! mesg_name ! callsign ! findMessage doEncrypt send ; PUBLIC pub-do-message-encrypt : pub-do-message-for ( message type encoded? radio -- ) caller mlevel 3 < if "Mucker Level 3 Required." abort then trigger ! encoded? ! mesg_type doMessage ; PUBLIC pub-do-message-for : pub-do-getlock ( channel -- lock 1 | 0 ) caller mlevel 2 < if "Mucker Level 2 Required." abort then channel ! prog "_radio_lock/" channel @ intostr strcat getprop dup not if pop 0 else 1 then ; (--- ENC PUBLIC INTERFACES ---) : defs MESG_TYPE_NONE intostr "MESG_TYPE_NONE" MESG_TYPE_SAY intostr "MESG_TYPE_SAY" MESG_TYPE_POSE intostr "MESG_TYPE_POSE" MESG_TYPE_SPOOF intostr "MESG_TYPE_SPOOF" MESG_TYPE_FSPOOF intostr "MESG_TYPE_FSPOOF" "#" prog int intostr strcat dup " \"pub-do-message\" call" strcat "do-radio-message" rot dup " \"pub-do-message-for\" call" strcat "do-radio-message-for" rot dup " \"pub-do-message-encrypt\" call" strcat "do-radio-message-encrypted" rot (dup) " \"pub-do-getlock\" call" strcat "do-radio-getlock" ; : doProgSetup me @ "W" flag? not if ">> You can't setup the communication proggie." .tell exit then prog "_setup?" getpropstr .yes? if ">> Already setup." .tell exit then 0 defs begin dup string? while "_defs/" swap strcat swap prog -3 rotate setprop repeat pop ">> _defs setup!" .tell prog "L" set ">> Program set LINK_OK." .tell prog "_setup?" "yes" setprop ">> Done." .tell ; : doSetup trigger @ "_channel" getpropstr dup if .ch_atoi channel ! removeReceiver else pop then ">> Setting channel to channel 0 and device to off." .tell 0 .ch_intostr channel ! trigger @ "_channel" "0" setprop trigger @ "_on?" remove_prop ">> Should this device NOT able to transmit signals?" .tell read strip .yes? if trigger @ "_recieve_only?" "yes" setprop else ">> What callsign should this device have? Type '.' to leave unset/unchanged." .tell read strip setcallsign then ; : main "me" match me ! (Faking required an M3.) me @ "~/radioban?" getpropstr .yes? if ">> You are BANNED from using this program. Prehaps you did something" .tell " wrong with it. Contact the wizstaff as to why." .tell exit then trigger @ "_channel" getpropstr dup not if doSetup exit then .ch_atoi channel ! dup not over "#h" stringpfx or if doHelp exit then BEGIN ( Setup jump-out point. ) dup 2 strcut pop "##" strcmp if dup "#" stringpfx if 1 strcut swap pop (We use short strings here to allow shortening of commands.) dup "on" stringpfx if pop do-on exit then checkOn if exit then dup "of" (off) stringpfx if pop do-off exit then dup "pset" (psetup) stringpfx if pop doProgSetup exit then dup "k" (key) stringpfx if " " .split swap pop setkey exit then dup "lock" stringpfx if " " .split swap pop setlock exit then dup "fo" (format) stringpfx if " " .split swap pop setformat exit then dup "ch" (channel) stringpfx if " " .split swap pop setChannel exit then dup "ca" (callsign) stringpfx if " " .split swap pop setcallsign exit then dup "sh" (showname) stringpfx if pop shownames exit then dup "hi" (hidename) stringpfx if pop hidenames exit then dup "e" (encrypt) stringpfx if trigger @ ".key" getpropstr not if ">> Key unset. Cannot transmit encrypted data." .tell then 1 encoded? ! trigger @ ".key" getpropstr key ! " " .split swap pop dup "##" 2 strncmp if dup "#" 1 strncmp not if 1 strcut swap pop else break then else 1 strcut swap pop break then then dup "sa" (say) stringpfx if " " .split swap pop MESG_TYPE_SAY mesg_type ! break then dup "po" (pose) stringpfx if " " .split swap pop MESG_TYPE_POSE mesg_type ! break then dup "sp" (spoof) stringpfx if " " .split swap pop MESG_TYPE_SPOOF mesg_type ! break then dup "fs" (freespoof) stringpfx if me @ "W" flag? not me @ "~radio_fspoof?" getpropstr .yes? or if ">> Only wizzies or people set ~radio_fspoof?:yes can freespoof!" .tell exit then " " .split swap pop MESG_TYPE_FSPOOF mesg_type ! then then else 1 strcut swap pop then BREAK REPEAT ( `BREAK' will go to next line.) checkOn if exit then trigger @ "_recieve_only?" getpropstr .yes? if ">> Device not equiped to transmit." .tell exit then doMessage ; . c q @set radio.muf=VIEWABLE @mpi {muf:radio.muf,#psetup}