SA-MP Forums

Go Back   SA-MP Forums > SA-MP Scripting and Plugins > Plugin Development

Reply
 
Thread Tools Display Modes
Old 25/03/2017, 06:36 PM   #381
WopsS
Huge Clucker
 
WopsS's Avatar
 
Join Date: Nov 2011
Posts: 497
Reputation: 70
Default Re: Gamemode SDK for C/C++ (GDK)

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
Code:
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
Code:
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.
WopsS is offline   Reply With Quote
Old 25/03/2017, 07:00 PM   #382
IstuntmanI
High-roller
 
IstuntmanI's Avatar
 
Join Date: Mar 2009
Location: Romania
Posts: 1,873
Reputation: 354
Default Re: Gamemode SDK for C/C++ (GDK)

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:
Code:
 * \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
Code:
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.
__________________


IstuntmanI is offline   Reply With Quote
Old 25/03/2017, 07:33 PM   #383
WopsS
Huge Clucker
 
WopsS's Avatar
 
Join Date: Nov 2011
Posts: 497
Reputation: 70
Default Re: Gamemode SDK for C/C++ (GDK)

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.
WopsS is offline   Reply With Quote
Old 25/03/2017, 09:06 PM   #384
WopsS
Huge Clucker
 
WopsS's Avatar
 
Join Date: Nov 2011
Posts: 497
Reputation: 70
Default Re: Gamemode SDK for C/C++ (GDK)

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

Code:
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

Code:
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:

Code:
int test = 10;
int test2 = 20;
int test3 = 30;

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

Code:
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

Code:
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

Code:
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

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

Code:
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.

Last edited by WopsS; 25/03/2017 at 10:54 PM.
WopsS is offline   Reply With Quote
Old 04/04/2017, 10:04 PM   #385
IstuntmanI
High-roller
 
IstuntmanI's Avatar
 
Join Date: Mar 2009
Location: Romania
Posts: 1,873
Reputation: 354
Default Re: Gamemode SDK for C/C++ (GDK)

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

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

Quote:
Originally Posted by WopsS View Post
...
This is for AttachDynamicObjectToObject
Code:
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:
Code:
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 View Post
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:
Code:
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 ) ... );
}
Code:
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 View Post
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:
Code:
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 Y_Less, I also used these methods to call the sscanf function:
Code:
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:
Code:
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 Y_Less 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.
__________________



Last edited by IstuntmanI; 06/04/2017 at 11:02 PM.
IstuntmanI is offline   Reply With Quote
Old 05/04/2017, 12:21 AM   #386
WopsS
Huge Clucker
 
WopsS's Avatar
 
Join Date: Nov 2011
Posts: 497
Reputation: 70
Default Re: Gamemode SDK for C/C++ (GDK)

Quote:
Originally Posted by IstuntmanI View Post
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
Code:
if (amx_FindPublic(amx, query->Callback.Name.c_str(), &amx_index) == AMX_ERR_NONE && amx_index >= 0)
try to replace it to

Code:
if (!amx_FindPublic(amx, query->Callback.Name.c_str(), &amx_index))
You need to recompile MySQL R39 from source.
WopsS is offline   Reply With Quote
Old 05/04/2017, 05:35 PM   #387
maddinat0r
Gangsta
 
maddinat0r's Avatar
 
Join Date: Jun 2010
Location: Germany
Posts: 884
Reputation: 435
Default Re: Gamemode SDK for C/C++ (GDK)

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.
maddinat0r is offline   Reply With Quote
Old 09/05/2017, 01:45 AM   #388
TheRealSphinx
Little Clucker
 
TheRealSphinx's Avatar
 
Join Date: Nov 2016
Posts: 20
Reputation: 10
Default Re: Gamemode SDK for C/C++ (GDK)

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
TheRealSphinx is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Gamemode is unknown. When i connect theres no gamemode. Shannon_Brown Server Support 11 09/09/2012 08:49 AM
[GameMode] Brasil Drift Show [BDS] - Primeiro GameMode vitorzero3um PortuguÍs/Portuguese 3 29/02/2012 06:48 PM
[ENG] Ideas for new GameMode [NL] Ideeen voor nieuwe Gamemode Xtreme-Nicole Everything and Nothing 10 10/07/2011 11:20 AM
[ENG] Ideas for new GameMode [NL] Ideeen voor nieuwe GameMode Xtreme-Nicole Help Archive 2 09/07/2011 11:24 AM


All times are GMT. The time now is 03:56 AM.


Powered by vBulletin® Version 3.8.6
Copyright ©2000 - 2017, Jelsoft Enterprises Ltd.