Gamemode SDK for C/C++ (GDK)

Quote:
Originally Posted by RaeF
Посмотреть сообщение
Have you defined the "native PrintFunc();" in your include or pwn file?
I wouldn't be able to compile the PAWN script otherwise.

Quote:
Originally Posted by Yashas
Посмотреть сообщение
@Konstantinos
Did you register the natives in AmxLoad? The script will be aware of the natives only when you register them. The register process basically fills the native functions table (the PAWN compiler leaves the address field as 0) with the correct addresses. If there is one or more function whose address isn't set, you'll get that error.
Indeed that was the problem. I was playing around with "helloworld" and when I decided to add some functions, forgot to add "AmxLoad" - thanks.
Reply

Does anyone manage to fix HTTP crash or find a way to use it ? I'm experiencing crashes or not calling at all. More you can find here https://github.com/Zeex/sampgdk/issues/164
Reply

Quote:
Originally Posted by doreto
Посмотреть сообщение
Does anyone manage to fix HTTP crash or find a way to use it ? I'm experiencing crashes or not calling at all. More you can find here https://github.com/Zeex/sampgdk/issues/164
Did you try to execute that code directly in PAWN? AFAIK HTTP native function is faulty.

Anyway, if you're coding a plugin, why don't you use an advanced library like curl?

// Are you sure HTTP parameters are correct? It seems you lack data param.
(index, type, url[], data[], callback[])
Reply

It has everything that is required while my callback isn't been called at all. I decided to use SAMP HTTP because of the familiarity and easy of use (cause I'm not so good with C+).
I'm sorry for late comment, but I have a rough week.
Reply

After following the instructions on the wiki for setting up a CMake project, every time I try to run the plugin, I get a
Код:
segmentation fault (core dumped)
error. I've tried using crashdetect, however it doesn't return anything meaningful:
Код:
[debug] Server crashed due to an unknown error
[debug] Native backtrace:
[debug] #0 f7390ca0 in _Z13GetStackTraceRSt6vectorI10StackFrameSaIS0_EEPv () from plugins/crashdetect.so
[debug] #1 f7389d22 in _ZN11CrashDetect20PrintNativeBacktraceERSoPv () from plugins/crashdetect.so
[debug] #2 f738a314 in _ZN11CrashDetect20PrintNativeBacktraceEPv () from plugins/crashdetect.so
[debug] #3 f738c964 in _ZN11CrashDetect11OnExceptionEPv () from plugins/crashdetect.so
[debug] #4 f73905cd in ?? () from plugins/crashdetect.so
[debug] #5 f7783bd0 in ?? ()
[debug] #6 f74f152f in ?? () from /lib32/libc.so.6
[debug] #7 f734b3b6 in ?? () from plugins/helloworld.so
[debug] #8 f73e5345 in bsearch () from /lib32/libc.so.6
[debug] #9 f734b42e in ?? () from plugins/helloworld.so
[debug] #10 f734b48b in ?? () from plugins/helloworld.so
[debug] #11 f734b5ae in sampgdk_callback_init () from plugins/helloworld.so
[debug] #12 f7347fde in sampgdk_module_init () from plugins/helloworld.so
[debug] #13 f734cac2 in ?? () from plugins/helloworld.so
[debug] #14 f734cb76 in ?? () from plugins/helloworld.so
[debug] #15 f734cce6 in sampgdk_Load () from plugins/helloworld.so
[debug] #16 f7347f5d in _ZN7sampgdk4LoadEPPv () from plugins/helloworld.so
[debug] #17 f7347ed9 in Load () from plugins/helloworld.so
[debug] #18 080d2742 in ?? () from ./samp03svr
[debug] #19 080d2afa in ?? () from ./samp03svr
[debug] #20 080aa0d0 in ?? () from ./samp03svr
[debug] #21 f73d0637 in __libc_start_main () from /lib32/libc.so.6
[debug] #22 0804b4e1 in ?? () from ./samp03svr
Reply

Great plugin!

Why isn't everyone using this?
Reply

Quote:
Originally Posted by Arturo226
Посмотреть сообщение
After following the instructions on the wiki for setting up a CMake project, every time I try to run the plugin, I get a
Код:
segmentation fault (core dumped)
error. I've tried using crashdetect, however it doesn't return anything meaningful:
Код:
[debug] Server crashed due to an unknown error
[debug] Native backtrace:
[debug] #0 f7390ca0 in _Z13GetStackTraceRSt6vectorI10StackFrameSaIS0_EEPv () from plugins/crashdetect.so
[debug] #1 f7389d22 in _ZN11CrashDetect20PrintNativeBacktraceERSoPv () from plugins/crashdetect.so
[debug] #2 f738a314 in _ZN11CrashDetect20PrintNativeBacktraceEPv () from plugins/crashdetect.so
[debug] #3 f738c964 in _ZN11CrashDetect11OnExceptionEPv () from plugins/crashdetect.so
[debug] #4 f73905cd in ?? () from plugins/crashdetect.so
[debug] #5 f7783bd0 in ?? ()
[debug] #6 f74f152f in ?? () from /lib32/libc.so.6
[debug] #7 f734b3b6 in ?? () from plugins/helloworld.so
[debug] #8 f73e5345 in bsearch () from /lib32/libc.so.6
[debug] #9 f734b42e in ?? () from plugins/helloworld.so
[debug] #10 f734b48b in ?? () from plugins/helloworld.so
[debug] #11 f734b5ae in sampgdk_callback_init () from plugins/helloworld.so
[debug] #12 f7347fde in sampgdk_module_init () from plugins/helloworld.so
[debug] #13 f734cac2 in ?? () from plugins/helloworld.so
[debug] #14 f734cb76 in ?? () from plugins/helloworld.so
[debug] #15 f734cce6 in sampgdk_Load () from plugins/helloworld.so
[debug] #16 f7347f5d in _ZN7sampgdk4LoadEPPv () from plugins/helloworld.so
[debug] #17 f7347ed9 in Load () from plugins/helloworld.so
[debug] #18 080d2742 in ?? () from ./samp03svr
[debug] #19 080d2afa in ?? () from ./samp03svr
[debug] #20 080aa0d0 in ?? () from ./samp03svr
[debug] #21 f73d0637 in __libc_start_main () from /lib32/libc.so.6
[debug] #22 0804b4e1 in ?? () from ./samp03svr
I think this has been fixed in the latest commits on the master branch.

Quote:
Originally Posted by cawfee
Посмотреть сообщение
Great plugin!

Why isn't everyone using this?
you didn't read the topic, did you? It's not a plugin.
Reply

Remove
Reply

I pretty much hate asking questions, but here we go, I'm searching for alternatives for these things for a few weeks already (well, since I started migrating my gamemode to C++ using sampGDK):

We can use sampgdk::InvokeNative to access functions called by other plugins. Is there any easy way to add a callback called by that plugin too ?
Using something similar to this (from sampgdk.c, amalgamation) is pretty long:
pawn Код:
typedef bool (SAMPGDK_CALLBACK_CALL *OnPlayerEditObject_callback)(int playerid, bool playerobject, int objectid, int response, float fX, float fY, float fZ, float fRotX, float fRotY, float fRotZ);
static bool _OnPlayerEditObject(AMX *amx, void *callback, cell *retval) {
  bool retval_;
  int playerid;
  bool playerobject;
  int objectid;
  int response;
  float fX;
  float fY;
  float fZ;
  float fRotX;
  float fRotY;
  float fRotZ;
  sampgdk_param_get_cell(amx, 0, (cell *)&playerid);
  sampgdk_param_get_bool(amx, 1, (bool *)&playerobject);
  sampgdk_param_get_cell(amx, 2, (cell *)&objectid);
  sampgdk_param_get_cell(amx, 3, (cell *)&response);
  sampgdk_param_get_float(amx, 4, (float *)&fX);
  sampgdk_param_get_float(amx, 5, (float *)&fY);
  sampgdk_param_get_float(amx, 6, (float *)&fZ);
  sampgdk_param_get_float(amx, 7, (float *)&fRotX);
  sampgdk_param_get_float(amx, 8, (float *)&fRotY);
  sampgdk_param_get_float(amx, 9, (float *)&fRotZ);
  sampgdk_log_debug("OnPlayerEditObject(%d, %d, %d, %d, %f, %f, %f, %f, %f, %f)", playerid, playerobject, objectid, response, fX, fY, fZ, fRotX, fRotY, fRotZ);
  retval_ = ((OnPlayerEditObject_callback)callback)(playerid, playerobject, objectid, response, fX, fY, fZ, fRotX, fRotY, fRotZ);
  if (retval != NULL) {
    *retval = (cell)retval_;
  }
  return !!retval_ != true;
}

<and some more lines in other parts>
I want something which auto generates the code, or something similar (I'll also add a macro for the InvokeNative native "generation", even if it takes ~5 lines anyway).

I want to be able to add external plugins easier, as close as possible to an actual Pawn .inc file.

Also, I tried to search for this, but I didn't find anything. A tool to automatically generate a .h and .cpp from a .idl file ( https://github.com/Zeex/sampgdk/tree/master/lib/sampgdk ) without trying to compile the whole plugin would be great. Compiling sampGDK looks pretty hard. Also, I read this topic: https://sampforum.blast.hk/showthread.php?tid=574029 and I remember that I failed to use the .lib version of sampGDK, I don't remember what was the problem more exactly (I tried this few weeks ago). It's a lot easier to use the amalgamated version, even if it isn't recommended.

For example, I can use these callbacks/natives things for the MySQL and Streamer plugins, to make something like a simple .inc file for each of them. I don't want to include them in my plugin (or using C++ MySQL connector with additional libraries, maddinat0r's MySQL has all I want) because it will make updating in the future harder.
Reply

I don't have a script to generate hpp and cpp files for you, but it is easy to invoke a function, you can write it in python and run it when you update the file.

This is for AttachDynamicObjectToObject
Код:
const bool Object::AttachToObject(int32_t ID, int32_t AttachToID, float X, float Y, float Z, float RotationX, float RotationY, float RotationZ, bool SyncRotation)
{
	static AMX_NATIVE Native = sampgdk::FindNative("AttachDynamicObjectToObject");
	return !!sampgdk::InvokeNative(Native, "iiffffffb", ID, AttachToID, X, Y, Z, RotationX, RotationY, RotationZ, SyncRotation);
}
and this is for GetDynamicObjectPos
Код:
const Point3D<float> Object::GetPosition(int32_t ID)
{
	Point3D<float> Restult;

	static AMX_NATIVE Native = sampgdk::FindNative("GetDynamicObjectPos");
	sampgdk::InvokeNative(Native, "iRRR", ID, &Restult.X, &Restult.Y, &Restult.Z);

	return Restult;
}
sampgdk will call OnPublicCall when a plugin call a callback (for example streamer will call OnPlayerPickUpDynamicPickup). You can take a look here to see how you can do that, https://github.com/Zeex/sampgdk/issues/155.

Of course you can add it to an IDL file and recompile the plugin, but this is the easy way.
Reply

Yeah, I saw examples for the InvokeNative function. Any example for variadic functions ? mysql_pquery, SetTimerEx or any other (for timers I'll try to implement Dan's plugin, with examples from sampGDK's timers). sampgdk.h:
Код:
 * \note In Pawn, variadic functions always take their variable arguments
 * (those represented by "...") by reference. This means that for such
 * functions you have to use the 'r' specifier where you would normally
 * use 'b', 'i' 'd' or 'f'.
I don't really get it, SetTimerEx would look like
Код:
const int SetTimerEx(const char * funcname, int interval, bool repeating, const char * format, ...)
{
	static AMX_NATIVE Native = sampgdk::FindNative("SetTimerEx");
	return sampgdk::InvokeNative(Native, "sibsr", funcname, interval, repeating, format, ???);
}
? I don't know what to put in place of those question marks. Maybe nothing ?

Totally forgot about OnPublicCall ! Thanks. I guess that it will be required to use static_cast for params of other types.

--------

By the way, about timers:
sampGDK's timers are way too basic, it would be great if they would be extended to look like Dan's Timerfix Plugin.
Also, I saw that we need to specify a globally created struct for the params. std::tuple wouldn't be better for this, without the need to create such a struct, or Zeex wants to keep compatibility with C compilers too ? std::tuple would be a lot better, with the std::make_tuple function.
Reply

I don't have anything in mind for mysql_pquery, but you can try to use a single parameter as a class or a struct.

When you call SetTimer you will pass a void* for parameters, in your function you need to know how to use that void*, that's why he is using a struct for that (you can use a class). This is the easy way. But if you want to use std::tuple go ahead, take a look here to see how you can achieve that.
Reply

Sorry for the double post, but here is a prototype for SetTimerEx.

Код:
template<typename... Args>
const int SetTimerEx(const std::string& func_name, const int interval, const bool repeating, const size_t total_arguments, const std::string& format, Args&& ...args)
{
	static AMX_NATIVE native = sampgdk::FindNative("SetTimerEx");
	return sampgdk::InvokeNative(native, (std::string("sibs") + std::string(total_arguments, 'r')).c_str(), func_name.c_str(), interval, repeating, format.c_str(), std::forward<Args>(args)...);
}
or

Код:
template<typename... Args>
const int SetTimerEx(const std::string& func_name, const int interval, const bool repeating, const std::string& format, Args&& ...args)
{
	static AMX_NATIVE native = sampgdk::FindNative("SetTimerEx");
	return sampgdk::InvokeNative(native, (std::string("sibs") + std::string(format.length(), 'r')).c_str(), func_name.c_str(), interval, repeating, format.c_str(), std::forward<Args>(args)...);
}
The second one is better since you don't need total_arguments anymore.

You can use it like this:

Код:
int test = 10;
int test2 = 20;
int test3 = 30;

SetTimerEx("TestTimer", 1000, true, 3, "iii", &test, &test2, &test3);
At OnPublicCall I have this

Код:
PLUGIN_EXPORT bool PLUGIN_CALL OnPublicCall(AMX *amx, const char *name, cell *params, cell *retval)
{
	auto Name = std::string(name);

	if (Name == "TestTimer")
	{
		std::cout << "TestTimer(" << params[1] << ", " << params[2] << ", " << params[3] << ")" << std::endl;
	}

	return true;
}
For floats use amx_ctof(param[index]).

// The only problem is with the strings, I cannot manage to get them, but maybe I'm doing it wrong.

I managed to get the strings using sampgdk_fakeamx_get_string and handle a special case for s. Now SetTimerEx looks like

Код:
template<typename... Args>
const int SetTimerEx(const std::string& func_name, const  int interval, const bool repeating, const std::string& format, Args&& ...args)
{
	static AMX_NATIVE native = sampgdk::FindNative("SetTimerEx");

	std::string function_format = "sibs";

	for (size_t i = 0; i < format.length(); i++)
	{
		function_format += format[i] == 's' ? "s" : "r";
	}

	return sampgdk::InvokeNative(native, function_format.c_str(), func_name.c_str(), interval, repeating, format.c_str(), std::forward<Args>(args)...);
}
and OnPublicCall

Код:
PLUGIN_EXPORT bool PLUGIN_CALL OnPublicCall(AMX *amx, const char *name, cell *params, cell *retval)
{
	auto Name = std::string(name);

	if (Name == "TestTimer")
	{
		char string[7];
		sampgdk_fakeamx_get_string(params[5], string, params[4] + 1);

		std::cout << "TestTimer(" << params[1] << ", " << params[2] << ", " << amx_ctof(params[3]) << ", \"" << string << "\")" << std::endl;
	}

	return true;
}
At the top of the file add

Код:
extern void sampgdk_fakeamx_get_string(cell address, char *dest, int size);
The call:

Код:
int test = 10;
int test2 = 20;
float test3 = 30.456f;
std::string test4 = "Hello!";
size_t test4_length = test4.length();
	
SetTimerEx("TestTimer", 1000, true, "iifis", &test, &test2, &test3, &test4_length, test4.c_str());
I can't find another way to pass strings for this function.

P.S: You need to pass the length of the string if you want to get it back.
Reply

I worked around these things, with help from your responses (thanks !) and now I came back with additional things:

--------------------------------------------------------
--------------------------------------------------------

Quote:
Originally Posted by WopsS
Посмотреть сообщение
...
This is for AttachDynamicObjectToObject
Код:
const bool Object::AttachToObject(int32_t ID, int32_t AttachToID, float X, float Y, float Z, float RotationX, float RotationY, float RotationZ, bool SyncRotation)
{
	static AMX_NATIVE Native = sampgdk::FindNative("AttachDynamicObjectToObject");
	return !!sampgdk::InvokeNative(Native, "iiffffffb", ID, AttachToID, X, Y, Z, RotationX, RotationY, RotationZ, SyncRotation);
}
...
Thanks for these examples, you gave me an idea to use namespaces for plugins functions. For example, CreateDynamicObject from streamer now looks like Plugins::Streamer::Object::Create now (a bit too long, but perfectly organised). Here is how it looks like, for who wants to see it:
Код:
namespace Plugins
{
	namespace Streamer
	{
		namespace Object
		{
				int Create( int modelid, float x, float y, float z, float rx, float ry, float rz, int worldid = -1, int interiorid = -1, int playerid = -1, float streamdistance = STREAMER_OBJECT_SD, float drawdistance = STREAMER_OBJECT_DD, int areaid = -1, int priority = 0 )
				{
					static AMX_NATIVE Native = sampgdk::FindNative( "CreateDynamicObject" );
					return sampgdk::InvokeNative( Native, "iffffffiiiffii", modelid, x, y, z, rx, ry, rz, worldid, interiorid, playerid, streamdistance, drawdistance, areaid, priority );
				}
		};
	};
};
--------------------------------------------------------

Quote:
Originally Posted by WopsS
Посмотреть сообщение
I don't have anything in mind for mysql_pquery, but you can try to use a single parameter as a class or a struct.
A problem: I made this:
Код:
template< typename... Args >
const bool pquery( int connectionHandle, const char query[ ], const char callback[ ] = "", const char format[ ] = "", Args && ... args )
{
	static AMX_NATIVE Native = sampgdk::FindNative( "mysql_pquery" );

	std::string function_format = "isss";

	for( int i = 0, s = std::strlen( format ); i < s; i ++ )
	{
		function_format += ( format[ i ] == 's' ? "s" : "r" );
	}

	return !!sampgdk::InvokeNative( Native, function_format.c_str( ), connectionHandle, query, callback, format, std::forward< Args >( args ) ... );
}
Код:
Plugins::MySQL::pquery( 1, "<query>", "Callback", "iii", &varint1, &varint2, &varint3 );
But the function is not called:
Quote:
Originally Posted by mysql_log.html
Log("00:19:31","mysql_pquery",4,"connection: 1, query: \"<query>\", callback: \"Callback\", format: \"iii\"",0);
Log("00:19:31","CMySQLQuery::Execute[OnQueryFinish]",4,"starting query execution",1);
Log("00:19:31","CMySQLQuery::Execute[Callback]",4,"starting query execution",1);
Log("00:19:31","CMySQLQuery::Execute[Callback]",4,"query was successfully executed within x.xxx milliseconds",1);
Log("00:19:31","CMySQLResult::CMySQLResult()",4,"c onstructor called",1);
OnPublicCall with the name of that Callback isn't called (I'm printing in console all calls with the parameters). I even added that callback in the .def file. By the way, is it needed to add callbacks called by functions like mysql_pquery in the .def file ? Is it a problem of the MySQL, or I can solve this somehow ? I could really use some informations about this thing. Also, thanks for the idea of using &&, allowing me to pass both lvalues and rvalues, I just understood why you used that instead of pointers as arguments and "args ..." in InvokeNative.

--------------------------------------------------------

Quote:
Originally Posted by WopsS
Посмотреть сообщение
When you call SetTimer you will pass a void* for parameters, in your function you need to know how to use that void*, that's why he is using a struct for that (you can use a class). This is the easy way. But if you want to use std::tuple go ahead, take a look here to see how you can achieve that.
Not that, I was asking if we can't make a more advanced timer system, using ProcessTick. Instead of passing some struct and doing retarded casts we could simply pass a std::tuple with a variable number of elements.
I actually did this, using variadic templates from newer C++ standards, which means that it is also type safe, as everything is known at compile time. Here is what it looks like:
Код:
Timers::SetTimerEx( TimerFunction, 1000, 0, std::make_tuple( playerid, 10.0, "hello" ) );

void TimerFunction( int timerid, std::tuple< int, float, char * > params )
{
    int playerid = std::get< 0 >( params );
    float posX = std::get< 1 >( params );
    char * message = std::get< 2 >( params );
}
C++ compiler is really powerful. Maybe I'll release this timer system. It is based on Dan's timer system. It's a lot better than sampGDK's timer system.

--------------------------------------------------------

By the way, if anyone is interested in keeping to use the sscanf of ******, I also used these methods to call the sscanf function:
Код:
namespace Plugins
{
	namespace SSCANF
	{
		template< typename... Args >
		const int unformat( const char data[ ], const char format[ ], Args * ... args )
		{
			static AMX_NATIVE Native = sampgdk::FindNative( "sscanf" );

			std::string function_format = "ss";

			for( int i = 0, s = std::strlen( format ); i < s; i ++ )
			{
				switch( format[ i ] )
				{
					case 's': function_format += "S[128]"; break;
					case 'i': case 'd': case 'c': case 'l': case 'b': case 'h': case 'x': case 'o': case 'n': case 'f': case 'g': case 'u': case 'q': case 'r': function_format += "R"; break;
					default: break;
				}
			}

			return sampgdk::InvokeNative( Native, function_format.c_str( ), data, format, args ... );
		}
	};
};
Usage:
Код:
Plugins::SSCANF::unformat( "Data 5.0 hello", "p< >s[5]fs[6]", varstring1, &varfloat, varstring2 );
I know that C has sscanf and this looks stupid, but I find the variant of ****** a lot better for SA-MP's needs. By the way, I know that my method of doing it won't work fine with all usage cases of sscanf, but it's better than nothing. It fits perfectly for my needs. I don't use sscanf's "a" specifier, kustom (I only used it in 4 cases, but I converted to "d" ) or other more advanced things. In future maybe I'll try to convert sscanf and include it in my gamemode.
Reply

Quote:
Originally Posted by IstuntmanI
Посмотреть сообщение
I worked around these things, with help from your responses (thanks !) and now I came back with additional things:
No problem.

About mysql_pquery I think it doesn't call your function because of this line
Код:
if (amx_FindPublic(amx, query->Callback.Name.c_str(), &amx_index) == AMX_ERR_NONE && amx_index >= 0)
try to replace it to

Код:
if (!amx_FindPublic(amx, query->Callback.Name.c_str(), &amx_index))
You need to recompile MySQL R39 from source.
Reply

Doing the change Wopss wrote is okay, here's the commit that introduced this.

However I'd suggest you incorporate the source of the MySQL plugin R40 or higher directly into your C++ gamemode. I've redesigned the internal API with the potential usage in a sampgdk gamemode back in my mind. Creating a connection, sending a query and processing the result is as easy as this:
Code:
CError<CHandle> handle_error;
Handle_t handle = CHandleManager::Get()->Create(
	"localhost", "root", "pass", "database",
	options, handle_error);

if (handle_error)
{
	// some error happened
	return 0;
}

Query_t query = CQuery::Create("SELECT * FROM stuff");
query->OnExecutionFinished([callback](ResultSet_t resultset)
{
	Result_t result = resultset->GetActiveResult();
	
	std::string stuff;
	result->GetRowDataByName(0, "fieldname", stuff);
	
	printf("stuff is %s\n", stuff.c_str());
});
handle->Execute(CHandle::ExecutionType::PARALLEL, query);
You basically just have to copy over (almost) all plugin source files into your project and link it with the MySQL C connector library. Oh, and your compiler should be C++11-compliant.
Reply

Quote:
Originally Posted by WopsS
View Post
Hello,

I want to leave a response here about Streamer with SAMPGDK. You can use it as xeeZ said here, but you don't need to invoke Streamer_CallbackHook since it is deprecated in version 2.7.8.

Why?
Because Incognito decided to use SAMPGDK for OnPlayerConnect and OnPlayerDisconnect.

All you need to do is to invoke CreateDynamicObject as xeeZ do in his gist.
Code:
int CreateDynamicObject(int modelid, float x, float y, float z, float rx, float ry, float rz, int worldid = -1, int interiorid = -1, int playerid = -1, float streamdistance = 200.0, float drawdistance = 0.0)
{
  static AMX_NATIVE native = sampgdk::FindNative("CreateDynamicObject");
  return sampgdk::InvokeNative(native, "iffffffiiiff", modelid, x, y, z, rx, ry, rz, worldid, interiorid, playerid, streamdistance, drawdistance);
}
And then call it.

Now, I want to know your opinion. If we want to use Streamer with SAMPGDK it is better to invoke a native (like the method above) or to combine streamer plugin with our project (which will be a pain in the a** with plugin updates and with the combination)?

By the way, can we register a Streamer's callback in our plugins?
I tried this
Code:
SAMPGDK_CALLBACK(bool, OnPlayerEditDynamicObject(int playerid, int  objectid, int  response, float x, float y, float z, float rx, float ry, float rz));
SAMPGDK_CALLBACK(bool, OnPlayerSelectDynamicObject(int playerid, int objectid, int modelid, float x, float y, float z));

PLUGIN_EXPORT bool PLUGIN_CALL OnPlayerEditDynamicObject(int playerid, int  objectid, int  response, float x, float y, float z, float rx, float ry, float rz)
{
	sampgdk::logprintf("OnPlayerEditDynamicObject: %i | %f | %f | %f", objectid, x, y, z);
	return true;
}

PLUGIN_EXPORT bool PLUGIN_CALL OnPlayerSelectDynamicObject(int playerid, int objectid, int modelid, float x, float y, float z)
{
	sampgdk::logprintf("OnPlayerSelectDynamicObject: %i | %f | %f | %f", objectid, x, y, z);
	return true;
}
And this functions aren't called, instead OnPlayerEditObject is called. If I edit an object.
Hi,

you can use this streamer version that supports direct implementation of the streamer in the gamemode.
Then you just need to call the events in your gamemode.

All needed events are listed in the header file.
https://github.com/Sphinxila/samp-pl...r/streamer.hpp

I think its self explanatory.
Feel free to use it or just ignore it ^^.

I rewrote this just for my usage cause "InvokeNative"...
It's not the point of using the SampGDK.

Kind regards
Sphinx
Reply

Zeex, I have one question. I really want to write my gamemode using SampGDK. But, as I understand, third-party plug-ins (such as Streamer, sscanf, mxINI, etc.) Can not be inserted into the GM simply #include "*plugin*", because the syntax of header files is incompatible with C++. How can I port it then?
Reply

https://github.com/IstuntmanI/samp-s...sampgdk-invoke for streamer (or just port it).
sscanf is already supplied in C.
mxINI... create your own INI parser or find one on github, there's tons.
Reply

Quote:
Originally Posted by Freshncool
View Post
https://github.com/IstuntmanI/samp-s...sampgdk-invoke for streamer (or just port it).
sscanf is already supplied in C.
mxINI... create your own INI parser or find one on github, there's tons.
There should be ways to link streamer function with your library.
In C++ there are better ways to interpret command arguments, without ever touching sscanf from the standard C library.
Reply


Forum Jump:


Users browsing this thread: 3 Guest(s)