Results 1 to 13 of 13

Thread: A little help with events in a multi-user environment, please?

  1. #1

    Thread Starter
    Fanatic Member ExcalibursZone's Avatar
    Join Date
    Feb 2000
    Location
    Western NY State
    Posts
    908

    Question

    I'm attempting to write a chat program that allows multiple users over telnet. The program works relatively well, it's object oriented (and still in the progress of converting
    the code). There are player objects which contain their own sockets, and command parsers (to help eliminate confusion and conflicts). For now, the objects are stored in
    an Array (IE Player() as new oPlayer).

    What I'm doing is using a large case statement to deal with commands, parse out the data that's to be sent to the other users in the chat program, and grab other
    parameters from the incoming data.

    Currently, when someone sends something that's seen by everyone, the server will cycle through the players who are online with a for next loop. (previously it was set to go
    to the maximum number of users each time, which slowed it down a bit). For instance:

    Code:
    For x = 1 to playercollection.count 
       y = playercollection.iIndex(x) 
       player(y).dataout = Message 
    next x
    As you can see, this would get rather boggy if more people logged on and there was alot of chatting going on. Going to maximum users all the time is a rather long
    process in itself.

    What I'd like to know, is if there is a way to utilize an event handler (this project has just modules and class modules, no forms) to take care of this problem. My thinking? If
    I can use events for commands, then i do not have to worry about problems dealing with invalid player objects stuck in the player collection. If this will work, all I have to
    do is raise the event Player_Chat or Player_Action and have the objects handle it themselves instead of cycling through a for next loop every time something happens.

    As I understand it, an object cannot handle an event that is declared within itself, but I can add an Event Handler object that contains the events to be triggered. I've tried
    to set up the error handler in a class module called PlayerEvents. Here is an example of my implementation, which didn't do what I needed it to.

    In class module PlayerEvents.cls:

    Code:
    Public Event Command(Command As String, Player As Index) 
    
    Public Sub parseCommand(pCommand As String, pPlayer as Integer) 
       RaiseEvent Command(pCommand,pPlayer) 
    End Sub
    In class module Player.cls:

    Code:
    Private WithEvents Socket as WinSock 
    Private WithEvents ehPlayer as PlayerEvents 
    
    Private Sub Class_Initialize() 
       Set ehPlayer = New PlayerEvents 
       'The code breaks without the above line 
       Set Socket = New WinSock 
    End Sub 
    
    Private Sub Socket_DataArrival(...) 
       ..... 
       Select Case LCase(left(sCommand,instr(1,sCommand," ")-1)) 
          Case "chat" 
             ehPlayer.parseCommand(sCommand,SocketIndex) 
       End Select 
    
    Private Sub ehPlayer_Command(Command As String, Index as Integer) 
       Socket.SendData parseit(Command) 
    End Sub
    The above code is a representation, I've obviously left out variables and such, and just showed how I was working with the events. With the above code, the event works,
    but only for the person who raised the event. I think it has something to do with Set ... = New ... statement. I don't remember where I saw it, but I thought I read something
    that stated that there is a problem with mulitple objects that contain an instance of the event handler object not being able to see the events generated by the other
    copies or some such... As you can see from this last statement, events have somewhat confused me heh

    The question now is, even though i can get a user to deal with his own events, is there a way to trigger an event in the other user objects so that when one person sends a
    command, the appropriate event will fire for all the others so they can recieve the message? A quick example of what I'm asking follows:

    There are 4 players: A, B, C, & D
    1 -- Player A sends the command: chat how is everyone.
    2 -- The player object recives this, and sends this command to the event handler object which raises the event PlayerChat.
    3 -- Having raised the event, the following happens.
    4 -- The Player objects for players B, C, & D see that the PlayerChat event has been triggered, in each object, the Handler_PlayerChat sub fires, sending: PlayerA: how is
    everyone. to Socket.SendData
    5 -- The player object for Player A sees the event has been triggered and the Handler_PlayerChat sub fires, sending: You Chat: how is everyone. to Socket.SendData

    I'm hoping someone can help me out as it's rather unstable to 1-rely on for ... next loops, especially if more players are logged on, and 2-there has been quite a bit of
    instability on behalf of the collection and for ... next loops which causes the server to crash.
    If I can get the event handler working, without causing conflicts with input and output, I will worry less about having to loop through everything as much as I am now.

    Update: I tried to use a Globally declared EventHandler to see if that would work and it didn't.

    Does _anyone_ have an idea they might suggest?

    Thanks for reading my book,
    Excalibur

  2. #2

    Thread Starter
    Fanatic Member ExcalibursZone's Avatar
    Join Date
    Feb 2000
    Location
    Western NY State
    Posts
    908

    Unhappy

    Doesn't anyone know if this can be done?

    One Event class shared with multiple objects. One event triggered all objects recieve the event (not just the originating object).

    Any information would be greatly helpful.

  3. #3

    Thread Starter
    Fanatic Member ExcalibursZone's Avatar
    Join Date
    Feb 2000
    Location
    Western NY State
    Posts
    908

    Angry

    Does anyone know of any other forums/message boards I could go to that might actually answer this question or offer some possible assistance? Thankee

  4. #4
    Hyperactive Member
    Join Date
    Jun 2000
    Location
    Auckland, NZ
    Posts
    411

    I will answer you

    It will take some time to read your entire post however.

    Regards
    Paul Lewis

  5. #5
    Guest
    Hello there,

    There is a sample app in VB that will raise events on all the client machines. Look on the MSDN site for VB DCOM Samples. I know it's there, I look on my machine but I can't find the demo.

    If you can't find it let me know here and I will see if I can locate it.

    Best,

  6. #6
    Hyperactive Member
    Join Date
    Jun 2000
    Location
    Auckland, NZ
    Posts
    411

    Short answer or long answer?

    Short answer is : NO.

    Long answer is : Sort of.

    In effect you are asking for something for free. What you
    are asking for is a way to have subs or functions in your
    runtime objects fired (simultaneously) when some other
    event occurs.

    But even if there was a black box answer for you, wherein
    you do not have to keep an array or collection, SOMETHING
    else will have to. This is the nature of things. You can't
    get something for nothing...

    Hence the short answer of NO.

    To work with this, you should forget about Events and
    trying to fit in with the RaiseEvent method. Use Interfaces
    instead. If you need help on that post another message.

    You will still have to parse the message and then you will
    be able to call the appropriate Interface method on each of
    your objects. At times, your objects will simply have no
    implementation or they will have a fast exit in their code
    for the given action.

    If you envisage thousands of users, and speed is more
    important that memory, then have lots of collections or
    arrays to hold the appropriate lists of players to perform
    the actions on. It is far faster to go through a collection
    of 500 "interested" players than to go through all 1000
    calling the same method on each (even if they have fast
    exits in their code).



    Now on to the long answer, which is shorter in text because
    I have not had a huge amount of experience in this area.

    USing the API, you can create things called Events (see the
    CreateEvent API and others). Using this in conjunction
    with something called anonymous pipes, you could have your
    main app spawn many child processes - each one with a pipe
    to the main app. Each of the children are told to
    WaitForMultipleObjects which effectively puts them to sleep
    until some event wakes them up again.


    Now the main app gets something from a player and using the
    Event that was created, calls SetEvent to tell the children
    that an event has occured. Only those children interested
    would wake up because they were waiting for this particular
    event.

    Upon waking, the children call PeekNamedPipe and WriteFile
    to read/write data to the parent.

    How this will help you - once you get over the learning
    curve - is that it will be more of a true multi-tasking
    application so that your OS will be able to handle the load
    far better (especially if running on NT). It will mean
    that you do not need to have any lists of objects at all
    (because the API has done this for you) and it will mean
    that only those objects that are truly interested will
    respond.


    Phew, I hope that was in some way useful..

    One final note here is that I would assume you can not get
    up to the thousands of users anyhow, because you would run
    out of sockets on the server end. How many do you envisage
    having?

    Cheers
    Paul Lewis

  7. #7

    Thread Starter
    Fanatic Member ExcalibursZone's Avatar
    Join Date
    Feb 2000
    Location
    Western NY State
    Posts
    908
    Thanks for the exhaustive reply you just gave, Paul.

    What this application is is actually a MUD or Multi-User Dungeon. There is going to be alot of activity going on due to Monsters roaming around, weather changes, and player actions.

    I see approximately 20 people on at one time possibly as high as 50 or so (I still have to stress test what I have so far).

    I'm currently using for next loops to send data to the other players, each of which are objects. Each player object contain their own command parser, color parser, socket, etc.

    There are no forms associated with the server yet, I will be setting up a form that will be shown for minor status information, but everything runs from Sub Main.

    The mud is actually very dynamic, being able to change nearly the entire look and feel of the mud without having to reload, restart, or recompile it. There are some minor hardcoded points, but those are being worked on.

    For the most part, the server works very well, mainly as a Chat World of types. There are over 100 rooms to start, several communication channels, and a level system in place (for security to sensitive commands & chat lines).

    I'm really trying to kill off the for next loop idea because it seems to me like a hindrance and pretty quirky bug and I've already ran into several instances where the mud hasn't worked properly because a socket disconnected improperly (still looking for that little freak of a bug though).

    I figured using events would be the best remedy for that because, as I understood it, I wouldn't need to worry as much about the dependence of a player's index in a collection, array, or whatnot. If the player is there, the event handlers would be listening and pick up on anything they needed.

    I'll look more into the API (gotta get a good api/oop book for vb, there isn't a good bookstore within 45 minutes of here) and look into that.

    I did, however, get an event to fire off, but it only worked for the player who triggered the event (bleh) ... The other player completly ignored the event ...

    If you have any further suggestions, I'd be glad to hear them.
    Again, thanks for replying.
    -Excalibur

  8. #8

    Thread Starter
    Fanatic Member ExcalibursZone's Avatar
    Join Date
    Feb 2000
    Location
    Western NY State
    Posts
    908
    Did I forget to mention that players who are on the game connect via Telnet? There is no client outside of standard windows telnet (minimum needed to connect ... a basic telnet application). So raising an event on another persons computer is pretty much a moot point Though, if I had written a client, the event that told the client something is there or someone did something would be: Socket_DataArrival and a large case statement *heh*

    Let me know if you want to see what I have so far (in terms of connecting to the server and taking a look around.) by emailing me.

  9. #9
    Hyperactive Member
    Join Date
    Jun 2000
    Location
    Auckland, NZ
    Posts
    411

    Mismatch

    You are thinking in terms of the Event which the API has,
    but the Event which VB has is not the same.

    but, if you wish to pursue this, in your Class_Initialize()
    for the player, drop the line
    Code:
       Set ehPlayer = New PlayerEvents
    Instead, replace it with
    Code:
      Set ehPlayer = gPlayerEvent
    Of course gPlayerEvent is a global you have already created. This would be better though as an explicit sub so that you choose which events your objects respond to.

    The attached example will show you how to do what you are trying to do properly.

    Code:
    ' myPlayer.cls
    Option Explicit
    
    Private WithEvents anEvent1 As myEvent1
    Private WithEvents anEvent2 As myEvent2
    
    Private mName As String
    
    Public Sub SetName(newName As String)
      mName = newName
    End Sub
    Private Sub anEvent1_SomethingHappened()
      Debug.Print "in the event1 handler for " & mName
    End Sub
    Private Sub anEvent2_SomethingHappened()
      Debug.Print "in the event2 handler for " & mName
    End Sub
    
    Public Sub SetEventHandler1(newEventHandler As myEvent1)
      Set anEvent1 = newEventHandler
    End Sub
    Public Sub SetEventHandler2(newEventHandler As myEvent2)
      Set anEvent2 = newEventHandler
    End Sub
    Code:
    'myEvent1.cls and myEvent2.cls (two different classes but
     same code so just copy / paste it twice into seperate
     class modules)
    
    Option Explicit
    
    Public Event SomethingHappened()
      
    Public Sub Test()
    
      RaiseEvent SomethingHappened
    
    End Sub
    Code:
    ' module1.bas
    Option Explicit
    
    
    Sub Main()
      Dim lEvent1 As myEvent1
      Dim lEvent2 As myEvent2
      
      Set lEvent1 = New myEvent1
      Set lEvent2 = New myEvent2
      
      Dim mCol As Collection
      Set mCol = New Collection
      Dim player As myPlayer
      
      Dim c As Integer
      For c = 1 To 5
        Set player = New myPlayer
        player.SetName "Player " & Str(c)
        ' the first set of players will respond to event 1
        player.SetEventHandler1 lEvent1
        mCol.Add player
      Next
      
      For c = 6 To 10
        Set player = New myPlayer
        player.SetName "Player " & Str(c)
        ' the second set of players will respond to event 2
        player.SetEventHandler2 lEvent2
        mCol.Add player
      Next
      
      Debug.Print "Testing Event 1"
      lEvent1.Test
      
      Debug.Print "Testing Event 2"
      lEvent2.Test
      
    
    End Sub
    With this, you will be able to go ahead with what you were doing. It will be just as good as the API solution in many ways. There is something I have against VB's Events though - I just can't recall at present

    Cheers
    Paul Lewis

  10. #10

    Thread Starter
    Fanatic Member ExcalibursZone's Avatar
    Join Date
    Feb 2000
    Location
    Western NY State
    Posts
    908
    I can see that something is happening, but let me see if I can figure this out right. Using your code as an example.

    What's happening in Module1:

    We create lEvent1 & lEvent2 as Event1 and Event2 types.
    We then set them as new instances of Event1 & Event2. And they are not created using the WithEvents flag (that is taken care of in the MyPlayer object).

    We then create the collection which holds the player objects, which in turn are derived from the MyPlayer object.
    (I never really liked collections heh I used an array)

    We create 5 instances of MyPlayer (1-5), set the players names to 'Player #' then we set each player's eventhandler1 to point to lEvent1, leaving the second eventhandler empty.
    We then add the player to the collection.

    We create another 5 instances of MyPlayer (6-10), set their names, then set their eventhandler2 to lEvent2, leaving eventhandler1 empty. then the player is added to the collection.

    We then test the events.

    lEvent1.Test Triggers the somethinghappened Event in MyEvent1.

    Which in turn triggers anEvent1_somethinghappened in players 1-5.

    lEvent2.test triggers the somethinghappened event in myevent2.

    Which in turn triggers anEvent2_somethinghappened in players 6-10.

    Now, this is where I get a little bit muddled. Don't you have to declare anevent1/anevent2 as New? or am I correct that that was causing my initial problems?

    From what I can tell, we create the events initially in SubMain, which is the Global Event handler.

    Now, this is where I run into the problem ... Sub Main() is not where the events actually trigger from. Sub Main() has a loop that it processes until it recieves a shutdown_server command (boolean) (do while shutdown_server <> true). All operation takes place within the player object itself with no assistance from the main program (aside from setting up the initial socket (player(0)) for listen, after that everything is thrown on the player socket).

    I *just* tested the code you gave me by triggering the event from within the MyPlayer object, which did trigger off successfully, though *whew* what a recursion

    Do you think this method is succeptable to lag, as I think I read something that nothing can happen after the event is over for a given object.

    Thanks for the reply! I certainly hope this will work (I'll be testing it out by tomorrow night in the mud proper (with 1 command to start)).

    Really appreciate the help!
    -Excalibur

  11. #11
    Hyperactive Member
    Join Date
    Jun 2000
    Location
    Auckland, NZ
    Posts
    411

    Your summary is right

    You can raise the event from anywhere if you make sure you
    call a method on the event object as per my example.

    I haven't heard of any problems, but then, I haven't looked
    either. I don't really use this myself.

    The order of processing is simple. Just imagine that each
    object registered as being interested in these events is in
    a long list. The event "triggering" is going to call the
    event method in each object one after the other.

    This works because each object registered with myEvent1 is
    looking at the very same event object. In your original
    code, you were creating a different object for each player,
    hence it would never work.

    Remember that when dealing with classes (actually instances
    of classes), we only ever pass by reference. So player in
    the example had it's SetEventHandler1 (or 2) method called,
    and the reference to the lEvent1 object was passed.

    Again, I hope it helps.
    Paul Lewis

  12. #12

    Thread Starter
    Fanatic Member ExcalibursZone's Avatar
    Join Date
    Feb 2000
    Location
    Western NY State
    Posts
    908
    I hope so too. I'm going to be implementing this in the next few days and will post on the success/failure of my attempt heh (only putting it in will see if it works with my current code). But, if it does, then HoooooYAH

    Thanks again for the help!
    -Excalibur

  13. #13

    Thread Starter
    Fanatic Member ExcalibursZone's Avatar
    Join Date
    Feb 2000
    Location
    Western NY State
    Posts
    908
    Hey hey!
    Well, I tried the code. It was a little tricky at first, but I found a way to do it. It now works and sets off events for every player when someone does a certain command I have to flesh out parameters and such, and I had to create a new sub in sub main called SetPlayer that took care of assigning indexes and such. Hopefully now I can create one parser and just pass the data to the socket.senddata!

    Woo Good bye For Next Loops!!!

    Thanks again, Paul.
    -Excalibur

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width