Skip to content

Fixed an issue where Non-networked entities could not be created. #342

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

CookStar
Copy link
Contributor

@CookStar CookStar commented Jul 14, 2020

This solves the problem of creating Non-networked entities.

This issue was discovered by @progre.

Code:

# Source.Python Imports
#   Entities
from entities.entity import BaseEntity
BaseEntity.create("info_player_terrorist")

Output:

[SP] Caught an Exception:
ValueError: Conversion from "BaseEntity" (<_entities._entity.BaseEntity object at 0xed1eb980>) to "Index" failed.

The above exception was the direct cause of the following exception:
SystemError: <Boost.Python.function object at 0x9be5020> returned a result with an error set

The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "../addons/source-python/packages/source-python/plugins/command.py", line 164, in load_plugin
    plugin = self.manager.load(plugin_name)
  File "../addons/source-python/packages/source-python/plugins/manager.py", line 194, in load
    plugin._load()
  File "../addons/source-python/packages/source-python/plugins/instance.py", line 74, in _load
    self.module = import_module(self.import_name)
  File "../addons/source-python/plugins/test/test.py", line 10, in <module>
    BaseEntity.create("info_player_terrorist")

or

Output:

SystemError: <built-in function __import__> returned a result with an error set

[SP] Caught an Exception:
Traceback (most recent call last):
  File "../addons/source-python/packages/source-python/plugins/command.py", line 164, in load_plugin
    plugin = self.manager.load(plugin_name)
  File "../addons/source-python/packages/source-python/plugins/manager.py", line 194, in load
    plugin._load()
  File "../addons/source-python/packages/source-python/plugins/instance.py", line 74, in _load
    self.module = import_module(self.import_name)
  File "../addons/source-python/plugins/test/test.py", line 10, in <module>
    BaseEntity.create("info_player_terrorist")

ValueError: Unable to make a 'BaseEntity' instance out of this 'info_player_terrorist' entity.

When getting the Index of a Non-networked entity, an exception will be raised, so we need to clear the error indicator.

#define CREATE_EXC_CONVERSION_FUNCTION_BASE_ENTITY(to_type, to_name, from_type, from_name) \
inline to_type Exc##to_name##From##from_name(from_type from) { \
to_type result; \
if (!to_name##From##from_name(from, result)) { \
str str_from = str( \
boost::shared_ptr<CBaseEntityWrapper>( \
(CBaseEntityWrapper *) from, \
&NeverDeleteDeleter<CBaseEntityWrapper *>)); \
const char* str_value = extract<const char*>(str_from); \
BOOST_RAISE_EXCEPTION(PyExc_ValueError, XSTRINGIFY(Conversion from #from_name (%s) to #to_name failed.), str_value); \
} \
return result; \
}

@jordanbriere
Copy link
Contributor

What's your sp info? I tested just now, and the following properly printed <_entities._entity.BaseEntity object at 0x1B503D40> for me:

from entities.entity import BaseEntity

print(BaseEntity.create('info_player_terrorist'))

@CookStar
Copy link
Contributor Author

CookStar commented Jul 14, 2020

I can reproduce it on both Linux and Windows.

Linux

IMPORTANT: Please copy the full output.
--------------------------------------------------------
Checksum      : fc1593d37b004161acf9d9f35103847f
Date          : 2020-07-14 16:50:54.772424
OS            : Linux-4.15.0-88-generic-x86_64-with-debian-buster-sid
Game          : csgo
SP version    : 698
Github commit : 2c9c421a280e5f7dce861af9bf1f31d8c08ab2c7
Server plugins:
   00: Source.Python, (C) 2012-2020, Source.Python Team.
SP plugins:
--------------------------------------------------------

Windows

IMPORTANT: Please copy the full output.
--------------------------------------------------------
Checksum      : 13f91a611c75806d9c39b93128668cb0
Date          : 2020-07-14 16:51:22.950771
OS            : Windows-10-10.0.18362
Game          : csgo
SP version    : 698
Github commit : 2c9c421a280e5f7dce861af9bf1f31d8c08ab2c7
Server plugins:
   00: Source.Python, (C) 2012-2020, Source.Python Team.
SP plugins:
--------------------------------------------------------

Others have also reproduced this problem.

@jordanbriere
Copy link
Contributor

I tested on CS:S, turns out that entity is networked on that game. I was able to reproduce using an info_node_link_controller.

This brought another point we should probably change as well, where while it is correct to silence all exceptions into the find method (since it is designed to never raise anyway), we should only filter the exceptions we expect into the create one. For example, the following class:

class Ent(BaseEntity):
    def __init__(self, index):
        1 / 0

    @classmethod
    def _obj(cls, ptr):
        1 / 0

Ent.create('info_player_terrorist')

Would currently raises the following:

ValueError: Unable to make a 'Ent' instance out of this 'info_player_terrorist' entity.

While it should really raises:

ZeroDivisionError: division by zero

The following should have the correct behaviour without silencing any and all exceptions that weren't expected:

object CBaseEntityWrapper::create(object cls, const char *name)
{
	object entity = object();
	CBaseEntityWrapper *pEntity = (CBaseEntityWrapper *)create(name);
	try
	{
		entity = cls(pEntity->GetIndex());
	}
	catch (...)
	{
		if (!PyErr_ExceptionMatches(PyExc_ValueError))
			throw_error_already_set();

		PyErr_Clear();
		try
		{
			CPointer tmp = pEntity->GetPointer();
			entity = MakeObject(cls, &tmp);
		}
		catch (...)
		{
			pEntity->remove();

			if (!PyErr_ExceptionMatches(PyExc_ValueError))
				throw_error_already_set();
			else
			{
				const char *classname = extract<const char *>(cls.attr("__qualname__"));
				BOOST_RAISE_EXCEPTION(
					PyExc_ValueError,
					"Unable to make a '%s' instance out of this '%s' entity.",
					classname, name
				)
			}
		}
	}
	return entity;
}

If it works as expected for you, please update your PR and I will merge it.

Thanks for the report, and the fix! 👍

@CookStar
Copy link
Contributor Author

CookStar commented Jul 14, 2020

That works as expected if you load the plugin by server.cfg, but if you load the plugin afterwards, it will have a different result.

Loaded via server.cfg:

[SP] Loading plugin 'test'...

[SP] Caught an Exception:
Traceback (most recent call last):
  File "../addons/source-python/plugins/test/test.py", line 17, in _obj
    1 / 0
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "../addons/source-python/packages/source-python/plugins/command.py", line 164, in load_plugin
    plugin = self.manager.load(plugin_name)
  File "../addons/source-python/packages/source-python/plugins/manager.py", line 194, in load
    plugin._load()
  File "../addons/source-python/packages/source-python/plugins/instance.py", line 74, in _load
    self.module = import_module(self.import_name)
  File "../addons/source-python/plugins/test/test.py", line 19, in <module>
    Ent.create("info_player_terrorist")

SystemError: <built-in function __import__> returned a result with an error set

Loaded from the terminal:

[SP] Loading plugin 'test'...

[SP] Caught an Exception:
Traceback (most recent call last):
  File "../addons/source-python/packages/source-python/plugins/command.py", line 164, in load_plugin
    plugin = self.manager.load(plugin_name)
  File "../addons/source-python/packages/source-python/plugins/manager.py", line 194, in load
    plugin._load()
  File "../addons/source-python/packages/source-python/plugins/instance.py", line 74, in _load
    self.module = import_module(self.import_name)
  File "../addons/source-python/plugins/test/test.py", line 19, in <module>
    Ent.create("info_player_terrorist")

SystemError: <Boost.Python.function object at 0xafedd60> returned NULL without setting an error

@jordanbriere
Copy link
Contributor

Hmm. That's actually interesting. The logic appears correct, but I suspect the fact of re-throwing the SystemError make it be caught by the nested catch which although it was cleared by then, attempt to re-throw it and returns NULL. I will merge your PR as is for now, and look further into this when I get the chance. Thanks again!

@jordanbriere jordanbriere merged commit 7e06ce8 into Source-Python-Dev-Team:master Jul 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants