MU Soapbox

    • Register
    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Muxify
    • Mustard

    Brilliant Breakthroughs and Impossible Projects

    MU Code
    7
    26
    5379
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Ashen-Shugar
      Ashen-Shugar last edited by

      I thought this may be a nice go-to topic for any aspiring MUSH developers or those of us who have been old timers and know our way around the block.

      What I would like is for this to be an open ended discussion and point/counter-point topic where everyone can bring up any challenging code snippit, project, or solution that they have worked on, are currently working on, or plan to work on in the future.

      Some of us have been doing this for well over 20 years, some have likely only have 20 hours. Let's combine all our knowledge and experience and help develop a gestalt learning and discussion forum that we can hopefully improve on all of our practices.

      Also, please try to keep heated debates about 'what is best' out of the picture. There'll always be dozens of right and wrong ways to do something. This should be more on how to better to something, how to solve issues we may have thought impossible, and to cross the bridge of knowledge and experience.

      Also, please feel free to provide multiple ways to do things. Since there are various MUSH platforms, each with their own unique feature sets, saying 'this is how I would do it on PennMUSH' then a follow up of 'This is how I would do it on RhostMUSH' or 'TinyMUSH' or 'MUX2' or Evennia or anything else would be greatly appreciated.

      Not only will this give alternatives, but it also will highlight the varying differences between the codebases as well as the differences in how we all approach a problem and work to a solution.

      So, with nothing else to say, let the code question and answers commence!

      1 Reply Last reply Reply Quote 0
      • faraday
        faraday last edited by faraday

        So I'm not sure if this is the right topic but it seems to kinda fit so I figured I'd try. Especially interested in @Thenomain and @Lithium's thoughts since we were talking about making the next-gen servers easier to learn.

        Ares plugins have Command Handlers - each responsible for a single command. You can think of them as the equivalent of &CMD-FOO attributes from Penn/Tiny/Rhost land.

        Now the easiest and most straightforward thing would be to have command handlers implement a single method to do everything.

        Side note: Ares uses Ruby, but what I have below is more psuedocode showing the general idea for simplicity. I want to focus on the overall flow, not the actual code.

        class PageCmd
           def on_command(client, cmd)
               # Figure out the parameters, like *=*
               recipients = cmd.args.arg1
               message = cmd.args.arg2
               # Do error checks
               if (a recipient is not valid)
                 client.emit "Don't know who you're trying to page."
                 return # do not continue
               end
               # Emit the page to everyone
               recipients.each { emit page to recipient }
               client.emit page message
        

        The pseudocode there is pretty short, but in reality it would be a bit longer.

        Now, in the current version of Ares I tried to make the coder's life easier by breaking this process up into three distinct steps with three distinct Ruby methods: crack parses the parameters, checks do the error checking and abort if there's a problem, handle executes the command if there are no problems.

        class PageCmd
          # Define member variables for your command parameters
          # so they're available throughout the class
          attr_accessor :recipients, :message
        
          def crack
             # Figure out the parameters, like *=*
             recipients = cmd.args.arg1
             message = cmd.args.arg2
          end
        
          # This is an error-check method, because it starts with check_
          # If it returns nil, all is well.  If it returns a message, that message gets
          # emitted to the client and the command is aborted.
          # You can have other check methods too checking different things
          def check_recipients
             if (a recipient is not valid)
                 return "Don't know who you're trying to page."
             end
             return nil  # All is well
          end 
        
          # All the error checks have passed, do the thing.
          def handle
              # Emit the page to everyone
              recipients.each { emit page to recipient }
              client.emit page message
          end
        

        I personally like the second example better. I think each step is broken out clearly. There's less repetition with the error check because the if (error) emit return stuff is bundled up in the check method handling.

        BUT is it too much for new devs to absorb? Functions that get auto-executed based on their names? Member variables? The emit/return stuff happening behind the scenes? I worry that in making it cleaner, it's actually made it harder to learn.

        Thoughts?

        Thenomain 1 Reply Last reply Reply Quote 0
        • Bobotron
          Bobotron last edited by

          I'll bite, I suppose.

          I'm working on a new BBS for MUSH, coded in such a way as to be compatible on Penn, Rhost, and MUX (but primarily written for Penn). This BBS will be use Myrddin's baseline features, but expand out from there with reply threading. This is meant to allow for discussion below a specific topic (you could reply to bboard 3/3). The BBS will also feature familiar locks from Myrddin's, as well as individual per-post locks. This came about as a theory bit because of my extreme hatred of AnomalyJobs, and having a threaded BBS, where all commands (except for wrappers like +apply and such that are shortcuts) seems like it could work as an alternative.

          So far it's working nicely, though I'm not completely finished yet.

          Ashen-Shugar 1 Reply Last reply Reply Quote 1
          • Thenomain
            Thenomain @faraday last edited by Thenomain

            @faraday

            I like the quasi throw/catch, but what order would these fire in? In my code methodology, I often use things like: if (x = target->thing) which I realize is horrible code but you get the idea. This may be an absolute Mush-ism, but sometimes you do state changes while checking validity.

            The answer could be "don't do that", which IMO is fine.

            “If you wish to make an apple pie from scratch, you must first invent the universe.”
            ― Carl Sagan, Cosmos

            faraday 1 Reply Last reply Reply Quote 0
            • faraday
              faraday @Thenomain last edited by faraday

              @Thenomain You can do more than one check per error-checker method if you need to sequence things. It's a tool not a religion 🙂

                    def check_can_set_actor
                      return nil if self.name == enactor_name
                      return nil if Actors.can_set_actor?(enactor)
                      return "You're not allowed to do that."
                    end
              

              Technically they are also run in sorted alphabetical order, but I think it's getting a bit too wacky if you rely on that. The only time that gets used is for some of the built-in checks like 'is logged in', which you can include with a single line of code:

              include CommandRequiresLogin

              You want to make sure they're logged in before you start checking for things like character permissions and whatnot, so it's important that it runs first.

              Edit to add -- Oh, I think I missed what you were saying about state changes. If you need to do some funky state-based stuff, you should probably put the error check inside of the handle method. Otherwise the state change wouldn't be persisted. The check methods are just a shortcut for simple atomic things that can short-circuit the processing early on.

              Thenomain 1 Reply Last reply Reply Quote 0
              • Thenomain
                Thenomain @faraday last edited by

                @faraday said in Brilliant Breakthroughs and Impossible Projects:

                It's a tool not a religion

                That's totally opposite the usual mood about code bases; you really are tired of this hobby, aren't you?

                😉

                “If you wish to make an apple pie from scratch, you must first invent the universe.”
                ― Carl Sagan, Cosmos

                faraday 1 Reply Last reply Reply Quote 1
                • faraday
                  faraday @Thenomain last edited by

                  @Thenomain said in Brilliant Breakthroughs and Impossible Projects:

                  That's totally opposite the usual mood about code bases; you really are tired of this hobby, aren't you?

                  Seriously, the number of code holy wars encountered in this hobby are but a pale shadow of the number encountered in my day to day job. But yes, so sick of all of them. 🙂

                  1 Reply Last reply Reply Quote 0
                  • Ashen-Shugar
                    Ashen-Shugar @Bobotron last edited by

                    @Bobotron said in Brilliant Breakthroughs and Impossible Projects:

                    I'll bite, I suppose.

                    I'm working on a new BBS for MUSH, coded in such a way as to be compatible on Penn, Rhost, and MUX (but primarily written for Penn). This BBS will be use Myrddin's baseline features, but expand out from there with reply threading. This is meant to allow for discussion below a specific topic (you could reply to bboard 3/3). The BBS will also feature familiar locks from Myrddin's, as well as individual per-post locks. This came about as a theory bit because of my extreme hatred of AnomalyJobs, and having a threaded BBS, where all commands (except for wrappers like +apply and such that are shortcuts) seems like it could work as an alternative.

                    So far it's working nicely, though I'm not completely finished yet.

                    When you play in Rhost, check out packmath(). It's something where you can apply math to a compressed RADIX number.

                    So like:

                    think pack(12345,36)
                    9IX
                    think packmath(9IX,36,70000,+)
                    1RJD
                    think unpack(1RJD,36)
                    82345
                    

                    It'd be a nice way to tweak your indexing without having to unpack the numbers first.

                    Bobotron 1 Reply Last reply Reply Quote 0
                    • Bobotron
                      Bobotron @Ashen-Shugar last edited by

                      @Ashen-Shugar

                      I'll look at that. Right now I have the method to mimic baseconv() from where you gave it to me on MUS*H, set up to make a baseconv() function for MUX and Rhost.

                      1 Reply Last reply Reply Quote 0
                      • Griatch
                        Griatch last edited by Griatch

                        @faraday

                        So if I understand you correctly, when the correct command key (or alias?) has been identified, it jumps to the given handler class and then fires in turn the cracks, checks and handle methods?

                        This sounds pretty reasonable to me, (although 'cracks' is a strange name to me - some sort of mush/ruby-ism?).
                        You should not be doing this for ease-of-use only though I think; it will then only increase complexity just as you fear. The main motivation should be to encourage handler inheritance: allowing devs to implement parsing for a whole group of commands at once.

                        In Evennia we do a similar thing; when a Command has been identified; we in turn call the Command's at_pre_cmd, parse, func and at_post_cmd -methods in turn. Of these, only parse and func are implemented with any regularity (the pre/post methods are needed by those wanting to plug in custom stuff without changing default command functionality).
                        The parse method was implemented once for almost all our "mux-like" commands and a second time for certain more advanced admin commands. Most devs never touch it but gets the parsing for free, implementing only func for every new Command, that is the actual actions performed on the already parsed input.

                        So as long as you emphasize that command handlers are classes that can be inherited, you can get rid of a lot of boiler plate for your users with parsing only needing to be coded once for big swathes of commands.
                        .
                        Griatch

                        1 Reply Last reply Reply Quote 0
                        • skew
                          skew last edited by

                          @Bobotron New BBS sounds cool 😄

                          @everyoneelse

                          I'm terrified of hooking the general "pose" and "say" commands. I ran into this issue while doing my +poseorder code, where I wanted to also include a +repose code, but the way I wanted to do it required hooking those commands.

                          So: Has anyone hooked the basic pose/say commands? Is there some big scary reason why we shouldn't be doing it?

                          (I am talking TinyMUX, but I'm sure it's similar-ish enough in Rhost and Penn.)

                          faraday Thenomain 2 Replies Last reply Reply Quote 0
                          • faraday
                            faraday @skew last edited by

                            @skew said in Brilliant Breakthroughs and Impossible Projects:

                            So: Has anyone hooked the basic pose/say commands? Is there some big scary reason why we shouldn't be doing it?

                            I've used it for my pose autospacer and combat pose tracking on Penn and it worked just fine. I know others have used it for pose order/repose stuff too without incident.

                            @Griatch Thanks for the feedback. I think we might be talking about two different levels of argument parsing with the parse/crack methods. Your parse sounds very generic if you can use one version for all MUX-like commands. Crack is highly command-specific, because what it does is take something like: <target>=<message> or <board #>/<post #> and break it into sensibly-named variables like "target", "message", "board_num", and "post_num". There's almost zero opportunity for inheritance there because almost every command has different arguments. There are utilities for common scenarios, but that's more composition than inheritance.

                            But yeah, we have a similar command flow with the base command handler's on_command method calling log, crack, error-check (aborting if any check_xxx method returns an error), and handle. Most handlers implement crack, handle, and at least one check, but there are exceptions. "who" just has handle. Some override log for privacy so we don't log things like pages and poses.

                            Griatch 1 Reply Last reply Reply Quote 0
                            • Griatch
                              Griatch @faraday last edited by Griatch

                              @faraday

                              naybe you are right and we work at different abstraction levels, yes. All Evennia hands over to the command is

                              cmdnameargs
                              

                              Where cmdname is the key or alias of the command used and args is everything after it, with no added space.

                              At least from what I learned about the base "MUX-like" command set we use in Evennia, some 80% of commands can efficiently be parsed hereon by understanding

                              cmdname[/switch[/switch]...] [arg[;alias]...] [= arg[;alias...],...]
                              

                              Splitting that into optional easy-to use components make's it easy to do any extra fluff on a case by case basis. As said we have one more base class (inheriting from this one) that also handles commands with syntax for assigning variables etc; but that's basically all we need, and all other default commands in Evennia inherits from this.
                              .
                              Griatch

                              faraday 1 Reply Last reply Reply Quote 1
                              • faraday
                                faraday @Griatch last edited by faraday

                                @Griatch said in Brilliant Breakthroughs and Impossible Projects:

                                Splitting that into optional easy-to use components make's it easy to do any extra fluff on a case by case basis.

                                Yeah definitely different layers of abstraction. The Ares engine does similar parsing to what you're talking about. crack is for figuring out args. So you just leave that up to individual commands to figure out, splitting the string as needed? Nothing wrong with that, btw, I just codified that as a separate step.

                                Griatch 1 Reply Last reply Reply Quote 0
                                • Griatch
                                  Griatch @faraday last edited by

                                  @faraday said in Brilliant Breakthroughs and Impossible Projects:

                                  @Griatch said in Brilliant Breakthroughs and Impossible Projects:

                                  Splitting that into optional easy-to use components make's it easy to do any extra fluff on a case by case basis.

                                  Yeah definitely different layers of abstraction. The Ares engine does similar parsing to what you're talking about. crack is for figuring out args. So you just leave that up to individual commands to figure out, splitting the string as needed? Nothing wrong with that, btw, I just codified that as a separate step.

                                  In principle every individual command class could exactly control it's own parsing, yes. In reality commands end up being parsed so similarly they all inherit one or two parsers with only very few requiring individual extra parsing on top of that.
                                  .
                                  Griatch

                                  1 Reply Last reply Reply Quote 1
                                  • Thenomain
                                    Thenomain @skew last edited by

                                    @skew said in Brilliant Breakthroughs and Impossible Projects:

                                    Has anyone hooked the basic pose/say commands?

                                    Yes. That's how "posebreak" works.

                                    “If you wish to make an apple pie from scratch, you must first invent the universe.”
                                    ― Carl Sagan, Cosmos

                                    skew 1 Reply Last reply Reply Quote 0
                                    • skew
                                      skew @Thenomain last edited by

                                      @Thenomain said in Brilliant Breakthroughs and Impossible Projects:

                                      @skew said in Brilliant Breakthroughs and Impossible Projects:

                                      Has anyone hooked the basic pose/say commands?

                                      Yes. That's how "posebreak" works.

                                      Sorry, hooked the entire command, ignored it, and added those commands to the soft code.

                                      Posebreak is just using before/after, not replacing.

                                      Thenomain 1 Reply Last reply Reply Quote 0
                                      • Griatch
                                        Griatch last edited by Griatch

                                        Evennia has something called inline functions which are functions provided by the game dev that an unprivileged user may embed in any return string. These functions will be evaluated anew for every recipient of the string. The system is intended for creating dynamic content and also supports nesting.

                                        Here is an example; a time zone converter. First a simplified example of the python function (made outside the game by the dev in a module the inlinefunc system looks for such functions):

                                        def mytime( timestring, timezone, session=None):
                                            # ... convert difference between session time zone and given time zone and return the converted time ...
                                            return converted_timestring
                                        

                                        Here, we ignore error-checking/validation for clarity. The first two arguments to mytime are input by the user while session is added by Evennia for every session receiving a string in question.

                                        A user may now embed mytime in any output string; for example ad I'm located in central Europe I could do something like this:

                                        page Foo = Hi, so I'll be around at $mytime(14:00, GMT+1) tomorrow.
                                        

                                        And user Foo, being on the US east coast (GMT-5 I think?) would see:

                                        Griatch pages: Hi, so I'll be around at 08:00 (GMT-5) tomorrow.
                                        

                                        I take it something like this is handled directly in softcode in mush bases.
                                        .
                                        Griatch

                                        1 Reply Last reply Reply Quote 0
                                        • Thenomain
                                          Thenomain @skew last edited by

                                          @skew said in Brilliant Breakthroughs and Impossible Projects:

                                          @Thenomain said in Brilliant Breakthroughs and Impossible Projects:

                                          @skew said in Brilliant Breakthroughs and Impossible Projects:

                                          Has anyone hooked the basic pose/say commands?

                                          Yes. That's how "posebreak" works.

                                          Sorry, hooked the entire command, ignored it, and added those commands to the soft code.

                                          Posebreak is just using before/after, not replacing.

                                          I believe on Eldritch I wrote a function called 'say()' which would take the arguments 'say( <sayer>, <pose-structure> )' and do all the work of working out the spacing, which is half of what say/pose does. All tabletalk ('TT') code has to do this anyhow. The trick would then be assuring parsing is maintained which, now that I think about it, shouldn't be horrible. Input as 'noparse', just before outputting apply 'objeval( %#, <blah> )', and cross your fingers.

                                          “If you wish to make an apple pie from scratch, you must first invent the universe.”
                                          ― Carl Sagan, Cosmos

                                          Griatch 1 Reply Last reply Reply Quote 0
                                          • Griatch
                                            Griatch @Thenomain last edited by

                                            @Thenomain said in Brilliant Breakthroughs and Impossible Projects:

                                            @skew said in Brilliant Breakthroughs and Impossible Projects:

                                            @Thenomain said in Brilliant Breakthroughs and Impossible Projects:

                                            @skew said in Brilliant Breakthroughs and Impossible Projects:

                                            Has anyone hooked the basic pose/say commands?

                                            Yes. That's how "posebreak" works.

                                            Sorry, hooked the entire command, ignored it, and added those commands to the soft code.

                                            Posebreak is just using before/after, not replacing.

                                            I believe on Eldritch I wrote a function called 'say()' which would take the arguments 'say( <sayer>, <pose-structure> )' and do all the work of working out the spacing, which is half of what say/pose does. All tabletalk ('TT') code has to do this anyhow. The trick would then be assuring parsing is maintained which, now that I think about it, shouldn't be horrible. Input as 'noparse', just before outputting apply 'objeval( %#, <blah> )', and cross your fingers.

                                            What does "table talk code" mean in this context?
                                            .
                                            Griatch

                                            Bobotron 1 Reply Last reply Reply Quote 0
                                            • 1
                                            • 2
                                            • 1 / 2
                                            • First post
                                              Last post