From 92b99963832840065f68c0ed2b8fc6a84cc8846d Mon Sep 17 00:00:00 2001 From: Ayuto22 Date: Wed, 15 Nov 2017 21:53:16 +0100 Subject: [PATCH 01/11] Added user message hooks --- .../developing/modules/messages.hooks.rst | 7 + .../developing/modules/messages.impl.rst | 7 + .../packages/source-python/messages/hooks.py | 221 ++++++++++++++++++ .../packages/source-python/messages/impl.py | 193 +++++++++++++++ 4 files changed, 428 insertions(+) create mode 100644 addons/source-python/docs/source-python/source/developing/modules/messages.hooks.rst create mode 100644 addons/source-python/docs/source-python/source/developing/modules/messages.impl.rst create mode 100644 addons/source-python/packages/source-python/messages/hooks.py create mode 100644 addons/source-python/packages/source-python/messages/impl.py diff --git a/addons/source-python/docs/source-python/source/developing/modules/messages.hooks.rst b/addons/source-python/docs/source-python/source/developing/modules/messages.hooks.rst new file mode 100644 index 000000000..34ed4517a --- /dev/null +++ b/addons/source-python/docs/source-python/source/developing/modules/messages.hooks.rst @@ -0,0 +1,7 @@ +messages.hooks module +====================== + +.. automodule:: messages.hooks + :members: + :undoc-members: + :show-inheritance: diff --git a/addons/source-python/docs/source-python/source/developing/modules/messages.impl.rst b/addons/source-python/docs/source-python/source/developing/modules/messages.impl.rst new file mode 100644 index 000000000..b6800a5d9 --- /dev/null +++ b/addons/source-python/docs/source-python/source/developing/modules/messages.impl.rst @@ -0,0 +1,7 @@ +messages.impl module +===================== + +.. automodule:: messages.impl + :members: + :undoc-members: + :show-inheritance: diff --git a/addons/source-python/packages/source-python/messages/hooks.py b/addons/source-python/packages/source-python/messages/hooks.py new file mode 100644 index 000000000..2f5369c46 --- /dev/null +++ b/addons/source-python/packages/source-python/messages/hooks.py @@ -0,0 +1,221 @@ +# ../messages/hooks.py + +"""Provides user message hooking functionality.""" + +# TODO: +# - Implement more user messages +# - Integrate all of this into the messages package. Also use the message +# implementations for the UserMessageCreator subclasses. +# - Add the possibility to modify the recipients. This will require us to +# replace the original recipient filter due to two reasons: +# 1. Modifying the recipient filter crashes on CS:GO +# 2. Modifying the recipient filter will crash if our MRecipientFilter +# implementation doesn't match the passed recipient filter structure + +# ============================================================================= +# >> IMPORTS +# ============================================================================= +# Python +# Collections +from collections import defaultdict + +# Source.Python +# Core +from core import AutoUnload +# Engines +from engines.server import engine_server +# Filters +from filters.recipients import BaseRecipientFilter +# Bitbuffers +from bitbuffers import BitBufferWrite +from bitbuffers import BitBufferRead +# Listeners +from listeners import ListenerManager +# Memory +from memory import make_object +from memory import get_virtual_function +from memory.hooks import PreHook +from memory.hooks import PostHook +# Messages +from messages import UserMessage +from messages import get_message_index +from messages import get_message_name +from messages.impl import get_user_message_impl + +if UserMessage.is_protobuf(): + from _messages import ProtobufMessage + + +# ============================================================================= +# >> ALL DECLARATION +# ============================================================================= +__all__ = ( + 'HookUserMessageCreatedBase', + 'HookBitBufferUserMessage', + 'HookProtobufUserMessage', + 'HookUserMessage', +) + + +# ============================================================================= +# >> GLOBAL VARIABLES +# ============================================================================= +_user_message_data = None + + +# ============================================================================= +# >> CLASSES +# ============================================================================= +class HookUserMessageCreatedBase(AutoUnload): + def __init__(self, user_message): + if isinstance(user_message, int): + index = user_message + elif isinstance(user_message, str): + index = get_message_index(user_message) + else: + raise TypeError( + 'Invalid type for "user_message". int or str required.') + + self.message_index = index + self.message_name = get_message_name(index) + self.callback = None + + # Verify that it's a valid index + if self.message_name is None: + raise ValueError(f'Invalid user message: {user_message}') + + + def __call__(self, callback): + if not callable(callback): + raise ValueError('Callback must be callable.') + + self.callback = callback + self.hooks[self.message_index].register_listener(callback) + + def _unload_instance(self): + if self.callback is None: + return + + self.hooks[self.message_index].unregister_listener(self.callback) + + @property + def hooks(self): + """Return all hooks for a user message. + + :rtype: ListenerManager + """ + raise NotImplementedError('Must be implemented by a subclass.') + + +class HookBitBufferUserMessage(HookUserMessageCreatedBase): + """Decorator to register a raw user message hook for bitbuffer messages.""" + + hooks = defaultdict(ListenerManager) + + +class HookProtobufUserMessage(HookUserMessageCreatedBase): + """Decorator to register a raw user message hook for protobuf messages.""" + + hooks = defaultdict(ListenerManager) + + +class HookUserMessage(HookUserMessageCreatedBase): + """Decorator to register a convenient user message hook.""" + + hooks = defaultdict(ListenerManager) + + def __init__(self, user_message): + super().__init__(user_message) + + # Verify that the user message is supported/implemented. This will + # raise a NotImplementedError if it isn't. + self.impl = get_user_message_impl(self.message_index) + + +# ============================================================================= +# >> HOOKS +# ============================================================================= +if UserMessage.is_protobuf(): + @PreHook(get_virtual_function(engine_server, 'SendUserMessage')) + def _pre_message_end(args): + message_index = args[2] + + user_message_hooks = HookUserMessage.hooks[message_index] + protobuf_user_message_hooks = HookProtobufUserMessage.hooks[message_index] + + # No need to do anything behind this if no listener is registered + if not user_message_hooks and not protobuf_user_message_hooks: + return + + recipients = make_object(BaseRecipientFilter, args[1]) + buffer = make_object(ProtobufMessage, args[3]) + + protobuf_user_message_hooks.notify(recipients, buffer) + + # No need to do anything behind this if no listener is registered + if not user_message_hooks: + return + + try: + impl = get_user_message_impl(message_index) + except NotImplementedError: + return + + data = impl.read(buffer) + user_message_hooks.notify(recipients, data) + + # Update buffer if data has been changed + if data.has_been_changed(): + impl.write(buffer, data) + +else: + @PostHook(get_virtual_function(engine_server, 'UserMessageBegin')) + def _post_user_message_begin(args, return_value): + global _user_message_data + _user_message_data = (args[1], args[2], return_value) + + @PreHook(get_virtual_function(engine_server, 'MessageEnd')) + def _pre_message_end(args): + # This happens when we initialize our hooks, while a user message is + # currently being created + if _user_message_data is None: + return + + recipients_ptr, message_index, buffer_write_ptr = _user_message_data + + # Retrieve the ListenerManager instances + user_message_hooks = HookUserMessage.hooks[message_index] + bitbuffer_user_message_hooks = HookBitBufferUserMessage.hooks[message_index] + + # No need to do anything behind this if no listener is registered + if not user_message_hooks and not bitbuffer_user_message_hooks: + return + + recipients = make_object(BaseRecipientFilter, recipients_ptr) + buffer_write = make_object(BitBufferWrite, buffer_write_ptr) + buffer_read = BitBufferRead(buffer_write, False) + + # For bitbuffers we need to make sure every callback starts reading and + # writing from the very first bit. + for callback in bitbuffer_user_message_hooks: + buffer_read.seek_to_bit(0) + buffer_write.seek_to_bit(0) + callback(recipients, buffer_read, buffer_write) + + # No need to do anything behind this if no listener is registered + if not user_message_hooks: + return + + try: + impl = get_user_message_impl(message_index) + except NotImplementedError: + return + + buffer_read.seek_to_bit(0) + data = impl.read(buffer_read) + user_message_hooks.notify(recipients, data) + + # Update buffer if data has been changed + if data.has_been_changed(): + buffer_write.seek_to_bit(0) + impl.write(buffer_write, data) diff --git a/addons/source-python/packages/source-python/messages/impl.py b/addons/source-python/packages/source-python/messages/impl.py new file mode 100644 index 000000000..8c3ff0bcc --- /dev/null +++ b/addons/source-python/packages/source-python/messages/impl.py @@ -0,0 +1,193 @@ +# ../messages/impl.py + +"""Provides user message implementations.""" + +# ============================================================================= +# >> IMPORTS +# ============================================================================= +# Source.Python +from messages import UserMessage +from messages import get_message_index + + +# ============================================================================= +# >> ALL DECLARATION +# ============================================================================= +__all__ = ( + 'UserMessageData', + 'UserMessageImpl', + 'SayText2Impl', + 'implemented_usermessages', + 'get_user_message_impl', +) + + +# ============================================================================= +# >> CLASSES +# ============================================================================= +class UserMessageData(dict): + """A dict that keeps track whether it has been modified.""" + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self._changed = False + + def __setitem__(self, index, value): + super().__setitem__(index, value) + self._changed = True + + def __setattr__(self, attr, value): + if attr == '_changed': + super().__setattr__(attr, value) + else: + self[attr] = value + + def has_been_changed(self): + """Return whether the data has been changed. + + :rtype: bool + """ + return self._changed + + __getattr__ = dict.__getitem__ + + +class UserMessageImpl(object): + """Base class for user message implementations.""" + + @staticmethod + def read_protobuf(buffer): + """Read data from the given buffer. + + :param ProtobufMessage buffer: + The buffer to read. + :rtype: UserMessageData + """ + raise NotImplementedError + + @staticmethod + def write_protobuf(buffer, data): + """Write data to the given buffer. + + :param ProtobufMessage buffer: + The buffer to write to. + :param UserMessageData data: + User message data that should be written to the buffer. + """ + raise NotImplementedError + + @staticmethod + def read_bitbuffer(buffer): + """Read data from the given buffer. + + :param BitBufferRead buffer: + The buffer to read. + :rtype: UserMessageData + """ + raise NotImplementedError + + @staticmethod + def write_bitbuffer(buffer, data): + """Write data to the given buffer. + + :param BitBufferWrite buffer: + The buffer to write to. + :param UserMessageData data: + User message data that should be written to the buffer. + """ + raise NotImplementedError + + @classmethod + def read(cls, buffer): + """A wrapper for :meth:`read_protobuf` and :meth:`read_bitbuffer`. + + :param buffer: + The buffer to read. + :rtype: UserMessageData + """ + if UserMessage.is_protobuf(): + return cls.read_protobuf(buffer) + + return cls.read_bitbuffer(buffer) + + @classmethod + def write(cls, buffer, data): + """A wrapper for :meth:`write_protobuf` and :meth:`write_bitbuffer`. + + :param buffer: + The buffer to write. + :rtype: UserMessageData + """ + if UserMessage.is_protobuf(): + cls.write_protobuf(buffer, data) + else: + cls.write_bitbuffer(buffer, data) + + +class SayText2Impl(UserMessageImpl): + """SayText2 implementation.""" + + @staticmethod + def read_protobuf(buffer): + return UserMessageData( + index=buffer.get_int32('ent_idx'), + chat=buffer.get_bool('chat'), + message=buffer.get_string('msg_name'), + param1=buffer.get_repeated_string('params', 0), + param2=buffer.get_repeated_string('params', 1), + param3=buffer.get_repeated_string('params', 2), + param4=buffer.get_repeated_string('params', 3)) + + @staticmethod + def write_protobuf(buffer, data): + buffer.set_string('msg_name', data.message) + buffer.set_bool('chat', data.chat) + buffer.set_int32('ent_idx', data.index) + buffer.set_repeated_string('params', 0, data.param1) + buffer.set_repeated_string('params', 1, data.param2) + buffer.set_repeated_string('params', 2, data.param3) + buffer.set_repeated_string('params', 3, data.param4) + + @staticmethod + def read_bitbuffer(buffer): + return UserMessageData( + index=buffer.read_byte(), + chat=buffer.read_byte(), + message=buffer.read_string(), + param1=buffer.read_string(), + param2=buffer.read_string(), + param3=buffer.read_string(), + param4=buffer.read_string()) + + @staticmethod + def write_bitbuffer(buffer, data): + buffer.write_byte(data.index) + buffer.write_byte(data.chat) + buffer.write_string(data.message) + buffer.write_string(data.param1) + buffer.write_string(data.param2) + buffer.write_string(data.param3) + buffer.write_string(data.param4) + + +# ============================================================================= +# >> FUNCTIONS +# ============================================================================= +implemented_usermessages = { + get_message_index('SayText2'): SayText2Impl +} + +def get_user_message_impl(msg_index): + """Return the user message implementation for the given message index. + + :param int msg_index: + The index of the user message. + :raise NotImplementedError: + Raised if the given user message is not implemented. + :rtype: UserMessageImpl + """ + try: + return implemented_usermessages[msg_index] + except KeyError: + raise NotImplementedError( + f'User message with index {msg_index} is not implemented.') From d1cd44ca82b2be4895da7d7a4c2d9803df60ea84 Mon Sep 17 00:00:00 2001 From: Ayuto22 Date: Wed, 15 Nov 2017 22:06:38 +0100 Subject: [PATCH 02/11] Fixed chaining hooks Added more documentation --- .../packages/source-python/messages/hooks.py | 30 ++++++++++++++++++- .../packages/source-python/messages/impl.py | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/addons/source-python/packages/source-python/messages/hooks.py b/addons/source-python/packages/source-python/messages/hooks.py index 2f5369c46..f4cd28a91 100644 --- a/addons/source-python/packages/source-python/messages/hooks.py +++ b/addons/source-python/packages/source-python/messages/hooks.py @@ -67,7 +67,18 @@ # >> CLASSES # ============================================================================= class HookUserMessageCreatedBase(AutoUnload): + """Base decorator for user message hooks.""" + def __init__(self, user_message): + """Create a new user message hook. + + :param int/str user_message: + The user message index or name to hook. + :raise TypeError: + Raised if ``user_message`` is not and int or str. + :raise ValueError: + Raised if the user message does not exist. + """ if isinstance(user_message, int): index = user_message elif isinstance(user_message, str): @@ -84,15 +95,24 @@ def __init__(self, user_message): if self.message_name is None: raise ValueError(f'Invalid user message: {user_message}') - def __call__(self, callback): + """Finalize the hook registration by registering the callback. + + :param object callback: + A callable object that will be called when a user message is + created. + :return: + The callback that has been passed. + """ if not callable(callback): raise ValueError('Callback must be callable.') self.callback = callback self.hooks[self.message_index].register_listener(callback) + return self.callback def _unload_instance(self): + """Unregister the user message hook.""" if self.callback is None: return @@ -125,6 +145,14 @@ class HookUserMessage(HookUserMessageCreatedBase): hooks = defaultdict(ListenerManager) def __init__(self, user_message): + """Create a new user message hook. + + :raise NotImplementedError: + Raised if the user message has not been implemented yet in + Source.Python. + + .. seealso:: :meth:`HookUserMessageCreatedBase.__init__` + """ super().__init__(user_message) # Verify that the user message is supported/implemented. This will diff --git a/addons/source-python/packages/source-python/messages/impl.py b/addons/source-python/packages/source-python/messages/impl.py index 8c3ff0bcc..361d9ef28 100644 --- a/addons/source-python/packages/source-python/messages/impl.py +++ b/addons/source-python/packages/source-python/messages/impl.py @@ -173,6 +173,7 @@ def write_bitbuffer(buffer, data): # ============================================================================= # >> FUNCTIONS # ============================================================================= +#: A dictionary that contains all implemented user messages. implemented_usermessages = { get_message_index('SayText2'): SayText2Impl } From a2f1cda05373a0a94ab938a38354d20eb01ccb74 Mon Sep 17 00:00:00 2001 From: Ayuto22 Date: Thu, 16 Nov 2017 18:24:05 +0100 Subject: [PATCH 03/11] Renamed HookUserMessageCreatedBase to HookUserMessageBase --- .../packages/source-python/messages/hooks.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/addons/source-python/packages/source-python/messages/hooks.py b/addons/source-python/packages/source-python/messages/hooks.py index f4cd28a91..88c14b7b2 100644 --- a/addons/source-python/packages/source-python/messages/hooks.py +++ b/addons/source-python/packages/source-python/messages/hooks.py @@ -50,7 +50,7 @@ # >> ALL DECLARATION # ============================================================================= __all__ = ( - 'HookUserMessageCreatedBase', + 'HookUserMessageBase', 'HookBitBufferUserMessage', 'HookProtobufUserMessage', 'HookUserMessage', @@ -66,7 +66,7 @@ # ============================================================================= # >> CLASSES # ============================================================================= -class HookUserMessageCreatedBase(AutoUnload): +class HookUserMessageBase(AutoUnload): """Base decorator for user message hooks.""" def __init__(self, user_message): @@ -127,19 +127,19 @@ def hooks(self): raise NotImplementedError('Must be implemented by a subclass.') -class HookBitBufferUserMessage(HookUserMessageCreatedBase): +class HookBitBufferUserMessage(HookUserMessageBase): """Decorator to register a raw user message hook for bitbuffer messages.""" hooks = defaultdict(ListenerManager) -class HookProtobufUserMessage(HookUserMessageCreatedBase): +class HookProtobufUserMessage(HookUserMessageBase): """Decorator to register a raw user message hook for protobuf messages.""" hooks = defaultdict(ListenerManager) -class HookUserMessage(HookUserMessageCreatedBase): +class HookUserMessage(HookUserMessageBase): """Decorator to register a convenient user message hook.""" hooks = defaultdict(ListenerManager) @@ -151,7 +151,7 @@ def __init__(self, user_message): Raised if the user message has not been implemented yet in Source.Python. - .. seealso:: :meth:`HookUserMessageCreatedBase.__init__` + .. seealso:: :meth:`HookUserMessageBase.__init__` """ super().__init__(user_message) From 623dbc29dae4dc6e7fc46ed9a5cc692a448f62b0 Mon Sep 17 00:00:00 2001 From: Ayuto22 Date: Sat, 18 Nov 2017 13:40:05 +0100 Subject: [PATCH 04/11] Added the possibility to modify recipients --- .../packages/source-python/messages/hooks.py | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/addons/source-python/packages/source-python/messages/hooks.py b/addons/source-python/packages/source-python/messages/hooks.py index 88c14b7b2..175ca827d 100644 --- a/addons/source-python/packages/source-python/messages/hooks.py +++ b/addons/source-python/packages/source-python/messages/hooks.py @@ -26,6 +26,7 @@ from engines.server import engine_server # Filters from filters.recipients import BaseRecipientFilter +from filters.recipients import RecipientFilter # Bitbuffers from bitbuffers import BitBufferWrite from bitbuffers import BitBufferRead @@ -61,6 +62,7 @@ # >> GLOBAL VARIABLES # ============================================================================= _user_message_data = None +_recipients = RecipientFilter() # ============================================================================= @@ -175,10 +177,14 @@ def _pre_message_end(args): if not user_message_hooks and not protobuf_user_message_hooks: return - recipients = make_object(BaseRecipientFilter, args[1]) + # Replace original recipients filter + tmp_recipients = make_object(BaseRecipientFilter, args[1]) + _recipients.update(*tuple(tmp_recipients), clear=True) + args[1] = _recipients + buffer = make_object(ProtobufMessage, args[3]) - protobuf_user_message_hooks.notify(recipients, buffer) + protobuf_user_message_hooks.notify(_recipients, buffer) # No need to do anything behind this if no listener is registered if not user_message_hooks: @@ -190,17 +196,24 @@ def _pre_message_end(args): return data = impl.read(buffer) - user_message_hooks.notify(recipients, data) + user_message_hooks.notify(_recipients, data) # Update buffer if data has been changed if data.has_been_changed(): impl.write(buffer, data) else: + @PreHook(get_virtual_function(engine_server, 'UserMessageBegin')) + def _pre_user_message_begin(args): + # Replace original recipients filter + tmp_recipients = make_object(BaseRecipientFilter, args[1]) + _recipients.update(*tuple(tmp_recipients), clear=True) + args[1] = _recipients + @PostHook(get_virtual_function(engine_server, 'UserMessageBegin')) def _post_user_message_begin(args, return_value): global _user_message_data - _user_message_data = (args[1], args[2], return_value) + _user_message_data = (args[2], return_value) @PreHook(get_virtual_function(engine_server, 'MessageEnd')) def _pre_message_end(args): @@ -209,7 +222,7 @@ def _pre_message_end(args): if _user_message_data is None: return - recipients_ptr, message_index, buffer_write_ptr = _user_message_data + message_index, buffer_write_ptr = _user_message_data # Retrieve the ListenerManager instances user_message_hooks = HookUserMessage.hooks[message_index] @@ -219,7 +232,6 @@ def _pre_message_end(args): if not user_message_hooks and not bitbuffer_user_message_hooks: return - recipients = make_object(BaseRecipientFilter, recipients_ptr) buffer_write = make_object(BitBufferWrite, buffer_write_ptr) buffer_read = BitBufferRead(buffer_write, False) @@ -228,7 +240,7 @@ def _pre_message_end(args): for callback in bitbuffer_user_message_hooks: buffer_read.seek_to_bit(0) buffer_write.seek_to_bit(0) - callback(recipients, buffer_read, buffer_write) + callback(_recipients, buffer_read, buffer_write) # No need to do anything behind this if no listener is registered if not user_message_hooks: @@ -241,7 +253,7 @@ def _pre_message_end(args): buffer_read.seek_to_bit(0) data = impl.read(buffer_read) - user_message_hooks.notify(recipients, data) + user_message_hooks.notify(_recipients, data) # Update buffer if data has been changed if data.has_been_changed(): From 798dba2a0318c0c26019dd43436bb49b76f235ca Mon Sep 17 00:00:00 2001 From: Ayuto22 Date: Sat, 18 Nov 2017 13:46:46 +0100 Subject: [PATCH 05/11] Updated TODOs --- .../packages/source-python/messages/hooks.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/addons/source-python/packages/source-python/messages/hooks.py b/addons/source-python/packages/source-python/messages/hooks.py index 175ca827d..8c2f0313e 100644 --- a/addons/source-python/packages/source-python/messages/hooks.py +++ b/addons/source-python/packages/source-python/messages/hooks.py @@ -4,13 +4,7 @@ # TODO: # - Implement more user messages -# - Integrate all of this into the messages package. Also use the message -# implementations for the UserMessageCreator subclasses. -# - Add the possibility to modify the recipients. This will require us to -# replace the original recipient filter due to two reasons: -# 1. Modifying the recipient filter crashes on CS:GO -# 2. Modifying the recipient filter will crash if our MRecipientFilter -# implementation doesn't match the passed recipient filter structure +# - Use these message implementations for the UserMessageCreator subclasses. # ============================================================================= # >> IMPORTS From 74e8a5f5a0ebf450f75c069382a8096f39c2716c Mon Sep 17 00:00:00 2001 From: Ayuto22 Date: Sat, 18 Nov 2017 14:37:58 +0100 Subject: [PATCH 06/11] Added ProtobufMessage.get_field_count() --- src/core/modules/messages/messages.h | 5 +++++ src/core/modules/messages/messages_wrap.cpp | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/src/core/modules/messages/messages.h b/src/core/modules/messages/messages.h index 22e3741db..0173858a7 100644 --- a/src/core/modules/messages/messages.h +++ b/src/core/modules/messages/messages.h @@ -103,6 +103,11 @@ } return enum_value; } + + static int GetFieldCount(google::protobuf::Message* pMessage, const char* field_name) + { + return pMessage->GetReflection()->FieldSize(*pMessage, GetFieldDescriptor(pMessage, field_name)); + } // ==================================================================== diff --git a/src/core/modules/messages/messages_wrap.cpp b/src/core/modules/messages/messages_wrap.cpp index 10022bfd4..57a6b5c9c 100644 --- a/src/core/modules/messages/messages_wrap.cpp +++ b/src/core/modules/messages/messages_wrap.cpp @@ -163,6 +163,11 @@ void export_protobuf_message(scope _messages) ProtobufMessage.def("serialize_to_array", &google::protobuf::Message::SerializeToArray); ProtobufMessage.def("parse_from_array", &google::protobuf::Message::ParseFromArray); + ProtobufMessage.def( + "get_field_count", + &CProtobufMessageExt::GetFieldCount, + "Return the number of elements of a repeated field.\n\n" + ":rtype: int"); ProtobufMessage.add_property("name", &google::protobuf::Message::GetTypeName); ProtobufMessage.add_property("debug_string", &google::protobuf::Message::DebugString); From f11451f5b7535f87434f2d29d14f6bb0c5ef07e0 Mon Sep 17 00:00:00 2001 From: Ayuto22 Date: Sat, 18 Nov 2017 14:38:53 +0100 Subject: [PATCH 07/11] Fixed bit position of buffer_write (caused problems) --- .../source-python/packages/source-python/messages/hooks.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/addons/source-python/packages/source-python/messages/hooks.py b/addons/source-python/packages/source-python/messages/hooks.py index 8c2f0313e..d3aee4a13 100644 --- a/addons/source-python/packages/source-python/messages/hooks.py +++ b/addons/source-python/packages/source-python/messages/hooks.py @@ -229,6 +229,8 @@ def _pre_message_end(args): buffer_write = make_object(BitBufferWrite, buffer_write_ptr) buffer_read = BitBufferRead(buffer_write, False) + org_current_bit = buffer_write.current_bit + # For bitbuffers we need to make sure every callback starts reading and # writing from the very first bit. for callback in bitbuffer_user_message_hooks: @@ -236,6 +238,11 @@ def _pre_message_end(args): buffer_write.seek_to_bit(0) callback(_recipients, buffer_read, buffer_write) + # If none of the above callbacks wrote to the buffer, we need to restore + # the current_bit to the original value. + if buffer_write.current_bit == 0: + buffer_write.seek_to_bit(org_current_bit) + # No need to do anything behind this if no listener is registered if not user_message_hooks: return From b9a9a9825d5dda749e5b54ff615afba348029589 Mon Sep 17 00:00:00 2001 From: Ayuto22 Date: Sat, 18 Nov 2017 14:39:07 +0100 Subject: [PATCH 08/11] Added VGUIMenu implementation --- .../packages/source-python/messages/impl.py | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/addons/source-python/packages/source-python/messages/impl.py b/addons/source-python/packages/source-python/messages/impl.py index 361d9ef28..f987cd9ad 100644 --- a/addons/source-python/packages/source-python/messages/impl.py +++ b/addons/source-python/packages/source-python/messages/impl.py @@ -2,6 +2,9 @@ """Provides user message implementations.""" +# TODO: +# - Clear ProtobufMessage before rewriting it + # ============================================================================= # >> IMPORTS # ============================================================================= @@ -170,12 +173,62 @@ def write_bitbuffer(buffer, data): buffer.write_string(data.param4) +class VGUIMenuImpl(UserMessageImpl): + """VGUIMenu implementation.""" + + @staticmethod + def read_protobuf(buffer): + subkeys = {} + for index in range(buffer.get_field_count('subkeys')): + message = buffer.get_repeated_message('subkeys', index) + subkeys[message.get_string('name')] = message.get_string('str') + + return UserMessageData( + name=buffer.get_string('name'), + show=buffer.get_bool('show'), + subkeys=subkeys) + + @staticmethod + def write_protobuf(buffer, data): + buffer.set_string('name', data.name) + buffer.set_bool('show', data.show) + for key, value in data.subkeys.items(): + temp_buffer = buffer.add_message('subkeys') + temp_buffer.set_string('name', key) + temp_buffer.set_string('str', value) + + @staticmethod + def read_bitbuffer(buffer): + name = buffer.read_string() + show = buffer.read_byte() + length = buffer.read_byte() + + subkeys = {} + for index in range(length): + subkeys[buffer.read_string()] = buffer.read_string() + + return UserMessageData( + name=name, + show=show, + subkeys=subkeys) + + @staticmethod + def write_bitbuffer(buffer, data): + buffer.write_string(data.name) + buffer.write_byte(data.show) + buffer.write_byte(len(data.subkeys)) + for key, value in data.subkeys.items(): + buffer.write_string(key) + buffer.write_string(value) + + # ============================================================================= # >> FUNCTIONS # ============================================================================= #: A dictionary that contains all implemented user messages. implemented_usermessages = { - get_message_index('SayText2'): SayText2Impl + get_message_index('SayText2'): SayText2Impl, + get_message_index('VGUIMenu'): VGUIMenuImpl, } def get_user_message_impl(msg_index): From 423f2d4290fa2bee6c3092bb57392b01ef76dba6 Mon Sep 17 00:00:00 2001 From: Ayuto22 Date: Sat, 18 Nov 2017 14:53:30 +0100 Subject: [PATCH 09/11] ProtobufMessage is now cleared before it's is rewritten --- .../source-python/packages/source-python/messages/hooks.py | 1 + addons/source-python/packages/source-python/messages/impl.py | 3 --- src/core/modules/messages/messages_wrap.cpp | 5 +++++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/addons/source-python/packages/source-python/messages/hooks.py b/addons/source-python/packages/source-python/messages/hooks.py index d3aee4a13..140061ef0 100644 --- a/addons/source-python/packages/source-python/messages/hooks.py +++ b/addons/source-python/packages/source-python/messages/hooks.py @@ -194,6 +194,7 @@ def _pre_message_end(args): # Update buffer if data has been changed if data.has_been_changed(): + buffer.clear() impl.write(buffer, data) else: diff --git a/addons/source-python/packages/source-python/messages/impl.py b/addons/source-python/packages/source-python/messages/impl.py index f987cd9ad..a1afe07ac 100644 --- a/addons/source-python/packages/source-python/messages/impl.py +++ b/addons/source-python/packages/source-python/messages/impl.py @@ -2,9 +2,6 @@ """Provides user message implementations.""" -# TODO: -# - Clear ProtobufMessage before rewriting it - # ============================================================================= # >> IMPORTS # ============================================================================= diff --git a/src/core/modules/messages/messages_wrap.cpp b/src/core/modules/messages/messages_wrap.cpp index 57a6b5c9c..a4951233f 100644 --- a/src/core/modules/messages/messages_wrap.cpp +++ b/src/core/modules/messages/messages_wrap.cpp @@ -169,6 +169,11 @@ void export_protobuf_message(scope _messages) "Return the number of elements of a repeated field.\n\n" ":rtype: int"); + ProtobufMessage.def( + "clear", + &google::protobuf::Message::Clear, + "Clear the message."); + ProtobufMessage.add_property("name", &google::protobuf::Message::GetTypeName); ProtobufMessage.add_property("debug_string", &google::protobuf::Message::DebugString); ProtobufMessage.add_property("byte_size", &google::protobuf::Message::ByteSize); From fce4db1d1514f9b939f53b2ad944f120d71e7fd1 Mon Sep 17 00:00:00 2001 From: Ayuto22 Date: Sat, 18 Nov 2017 14:55:27 +0100 Subject: [PATCH 10/11] Renamed a function --- addons/source-python/packages/source-python/messages/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/source-python/packages/source-python/messages/hooks.py b/addons/source-python/packages/source-python/messages/hooks.py index 140061ef0..62cd1cff1 100644 --- a/addons/source-python/packages/source-python/messages/hooks.py +++ b/addons/source-python/packages/source-python/messages/hooks.py @@ -161,7 +161,7 @@ def __init__(self, user_message): # ============================================================================= if UserMessage.is_protobuf(): @PreHook(get_virtual_function(engine_server, 'SendUserMessage')) - def _pre_message_end(args): + def _pre_send_user_message(args): message_index = args[2] user_message_hooks = HookUserMessage.hooks[message_index] From a3270f04b161a85d87333fcb1148342a245c6a19 Mon Sep 17 00:00:00 2001 From: Ayuto22 Date: Sat, 18 Nov 2017 16:12:05 +0100 Subject: [PATCH 11/11] Added missing entry in __all__ --- addons/source-python/packages/source-python/messages/impl.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/source-python/packages/source-python/messages/impl.py b/addons/source-python/packages/source-python/messages/impl.py index a1afe07ac..11111f257 100644 --- a/addons/source-python/packages/source-python/messages/impl.py +++ b/addons/source-python/packages/source-python/messages/impl.py @@ -17,6 +17,7 @@ 'UserMessageData', 'UserMessageImpl', 'SayText2Impl', + 'VGUIMenuImpl', 'implemented_usermessages', 'get_user_message_impl', )