Hi!

Enus Linden wrote:
moving this to the list. (I took the liberty of pasting this mail's predecessor below as well)

Thanks, Enus :)
And good to see that we seem to be reaching some consensus.

Following are my attempts to answer the questions raised:

[...]

Similarly, the server can make any packet ackable (by setting the send flag with the ack flag), and so we must ack it, otherwise the server gets angry. We don't know which packet we will need to ack, we have to determine that when we receive one.

Ok, so that seems to be part of what a connection needs to handle.

It would follow the pattern seen in e.g. smtplib, urllib2 (where the Request is the message). And most network modules actually have a connection object, such as ftplib, nntplib, gopherlib etc. Not all have message classes though because if it's just a file you send, then there is no need to encapsulate this in a separate object.

Here also is another example of message objects in email:

http://docs.python.org/lib/message-objects.html

I'm starting to like the idea of a Message. Maybe this Message could be only the payload of the message, with a Packet class (I think you have suggested this) having the other necessary fields. Then, a serializer can serialize the packet and a net framework can send it on a connection.

Right, if you do ISerialization(packet) you will get the packet serializer which in turn can do a ISerialization(msg) to get the message serializer. It would then add everything togethet to one binary blob.

Also be aware that connections will change during the lifetime of the client. You don't have a single udp connection. You communication with neighboring sims, you may switch regions, etc. This causes you to create a new connection to send on.

On a higher level I was thinking about having different Region objects which all have their UDP and HTTP/Cap connection.

But also remember that for udp messages you don't need a connection, you can simply send the message to any given host. So, it may be extra to have a connection class doing such a thing because you can use a single connection to send and receive on. The target we are sending to changes, but we don't need to change the sockets or anything.

To me that would mean I need to know that it's a socket connection. On a higher level though I might not want or need to know. It's more a logical connection to that region, whatever the implementation.



*Current Design: *
This is taken from http://wiki.secondlife.com/wiki/Pyogp/Documentation/Specification/pyogp.lib.base

messenger = MessageSystem()
host = Host('sim_ip', 'sim_port') #note: these aren't true values, of course
messenger.new_message("UseCircuitCode")
messenger.next_block("CircuitCode")
messenger.add_data('Code', circuit_code, \
MsgType.MVT_U32)
messenger.add_data('SessionID', \
uuid.UUID(session_id), \
MsgType.MVT_LLUUID)
messenger.add_data('ID', \
uuid.UUID(agent_id), \
MsgType.MVT_LLUUID)
messenger.send_message(host)

_Explanation:_
The thing to know about the current design is that it is encapsulated into a MessageSystem. Everything from building, reading, sending, and receiving messages all occurs in the Message System (though each of the sets of functionality are performed by other objects that the system HAS).

I think this is a quite good explanation where we differ. As said before this feels very uncommon in the python world to me.

One concern I also have is all the sort of global state in these long living objects. It doesn't need to but might lead to problems with threading or coroutines. I would try to keep locking zones as small as possible. I also might think of this scenario with coroutines:

- You create and send a message in coroutine A
- Sending blocks for whatever reasons
- Coroutine B gets activated, creates a message and sends it. Maybe with the same message system. It also blocks on sending.
- current_msg is now message of B and this is what A sends.

So this would mean that you need to separate message system per thread. This also means though that it's only one host you connect to per message system and thus the host could be in the constructor as it's quite fixed then.

Yea, I do agree that it is confusing having the message remembered in state by the system, builders, and readers. I'm starting to like the idea of outputting a Message that the user adds data to and sends. Maybe the Message System could remain as the connection you send through and receive on, which automatically serializes sending packets and deserializes receiving packets, keep track of all acks and such.

Seems like it's basically doing the work already so I think it makes sense. Not sure what it should be called but we can discuss this later. But from a logical view on it it seems mostly in there although I would move some stuff to the Packet class and it's serializer (what you also suggested I think).


In Python you don't care about this. If there is a 1 you mean 1 and you don't care how it's sent over the wire on a lower level of the system. Yes, you might run into a problem if you don't know the type but in my experience this rarely actually leads into problems. Having no type also makes coding faster as you have to type less and you don't have to consult the documentation.
So let's get rid of the type-checking, I'm fine with that. It IS just extra junk I don't feel like typing anyway :)

Cool :-)


There might then also be a MessageDispatcher which does the same so it knows over which channel to send this message (I guess for XML messages it's simply the cap we have and we do cap.POST(data).
Right, so I'm thinking the Message System could do all this. Maybe the Message System could be the factory and dispatcher, with all messaging being sent and received going through it (but BUILDING messages not going through this).

In my branch I made the factory a utility as well but that's just a proposal. If we see it as MessageSystem and not as connection I guess it can also implement the message creation. If we see it as a connection IMHO it's strange to say that a connection produces messages. I would more think that a connection really just cares about serializing and deserializing and keeping track of state (acks) but not about how to create new messages.. They either get created by the system who wants to send it via that connection or they get somehow created on the other side of the network and it just converts them back from binary from to Python structures.

Maybe not too important though.


[...]

message (up to 500 I believe) will have its own unique class that will initialize the data attributes.

We would start with the ones we actually use in the library. If somebody needs to use an additional one he can still use the more low level version (Message('name', Block(...), Block(...)) ).

We also have to look at every message in the protocol spec anyway and define it there in detail. When we do this we can go along and define them in code as well. I am also willing to do that.

A pro here would be that you can put default values in the class so that you don't have to specify all parameters.

When receiving a message you would have the possibility to attach an event handler directly to that class using ZCA.

Another pro is that the user of this level doesn't have to know about blocks and the sequence of these. She only needs to know about the actual data to be passed in.
Well, we can do this with ZCA without deriving a class for each message. We can have them all implement an interface and register them with a certain name. This way we don't have to write each individual class, but can have a generic Message which can handle them all, with handlers. The default data can be added in by the Message Factory (which looks into the template and fills in the message with default values). I guess this is the problem then. If we have a single Message which builds itself (add_block methods), we cannot write a Message class which tests that the data being added is correct and expected. Unless the message itself is a UDP message derivation and can look at the message template itself and do the checking.

I would propose that for now we don't think about such high level classes. You also raised the issue yesterday of how those are deserialized and this probably needs some thinking.

If we have a structure like Message(Block('name', param1=10, param2=20)) it's maybe readable enough for now.

We might discuss which layers to add later then and such classes should probably also be part of a separate package.

I might write another mail with some proposals on how to maybe get a release out and what we should do before that (like cleaning up what we have, doing logging, exceptions etc.)


Some Connection class which seems to me similar to your message system and circuits.

BTW, what actually is a circuit? Is it a connection to a region? Or can you have many circuits to one region? This part of the protocol is not that clear to me right now. We probably should write it down if it isn't somewhere (but it should be part of the spec at some point anyway).
A circuit is a UDP connection. So, it is a UNIQUE connection to ip address and port combination. Can only have 1 circuit for each ip and port combination.

Sounds also like some connection then. I wonder then if MessageSystem and Circuit could be merged somehow and the circuit bit is just an implementation detail of the Connection class. But I am not into that code so that might be wrong.

Thanks for your work, I'm starting to see where we can improve things. I'll start writing down my new proposal and see if we can get something working.

Thanks for writing down that proposal! I will also be looking at your branch once you think it's lookworthy ;-) And we should keep the discussison going I think to get the best result we can.

PS: I don't like the idea of ZCAifying things like the dictionary just so that we can register them with ZCA as a global utility. It is an extra abstraction that is confusing and the reasoning not clearly seen. Something else we can do?

Well, this seemed to me as one of the main examples of using utilities because:

- they are singletons (and thus global)
- zope uses similar utilities a lot (like MessageCatalog/Translation)
- they only get instantiated once and the reading is only done once
- you don't need to pass them around
- you can still change them and override the utility if you think you need to.


Another approach would be to use a module level variable which holds the instantiated dictionary. You could import it with

import message_template
tmpldict = message_template.tmpldict

and you could also still override it in your own code by "patching" the module.

But I would prefer to only use one variant because otherwise it really gets confusing and you never know which variant is used now (and the deserializer is also a utility btw. for the same reason of being simply a global service).

Maybe it needs to be documented more? (I guess the answer to such question is never No ;-) ).

Speaking of docs, I will try to put some more stuff on the wiki this weekend, like explaining how to use grok, how to use exceptions, the logger etc.

cheers,

Christian



--
Christian Scholz                          Homepage: http://comlounge.net
COM.lounge                                   blog: http://mrtopf.de/blog
Luetticher Strasse 10                                    Skype: HerrTopf
52064 Aachen                             Video Blog: http://comlounge.tv
Tel: +49 241 400 730 0                           E-Mail [EMAIL PROTECTED]
Fax: +49 241 979 00 850                               IRC: MrTopf, Tao_T

neue Show: TOPFtäglich (http://mrtopf.de/blog/category/topf-taglich/)

_______________________________________________
Click here to unsubscribe or manage your list subscription:
https://lists.secondlife.com/cgi-bin/mailman/listinfo/pyogp

Reply via email to