[Tutorial]The first VC-MP Server built as plugin

Started by habi, Jun 10, 2022, 08:51 AM

Previous topic - Next topic

habi

Hello everyone.
I wrote a vc-mp server not in squirrel, not in java, not in lua and not in python. This server is very simple and scripted in C.

Here it goes

vcmpsrvr.c
[noae][noae][noae][noae][noae][noae][noae]#include "PluginAPI.h"
#include <stdio.h> //For printing text on console
#include <time.h> //For printing time on console
#include <string.h> //For calculating length of
#include <math.h> //For calculating distance between players
#define MAX_VEHICLES 1000
PluginFuncs* Server;
uint8_t fn_OnServerInitialise (void)
{
Server->SetServerName("Simple VC-MP Server");
Server->SetGameModeText("Friendly");
Server->SetServerOption(vcmpServerOptionFriendlyFire, 0);
Server->SetTimeRate(1000);
printf("-----------------------------------------------\n");
printf(" VCMP Server in C by habi, Banaqs (vehicles) \n");
printf("Date last scripted: 06 Jun 2022 \n");
printf("Creating Vehicles ...");
Server->CreateVehicle(130,0,-598.1232,622.5588,11.7840,1.604224,18,74);
Server->CreateVehicle(130,0,-598.6165,618.5028,11.7826,1.601955,18,74);
Server->CreateVehicle(166,0,-616.4828,651.4387,10.5840,6.085310,14,75);
Server->CreateVehicle(166,0,-611.2023,651.9318,10.5876,5.827294,14,75);

printf("Done\n");short wVehicleCount=0;
for(int i=1; i<MAX_VEHICLES;i++)
if(Server->CheckEntityExists(vcmpEntityPoolVehicle, i))
wVehicleCount++;
printf("Loaded %d vehicles\n", wVehicleCount);
printf("Commands: /veh /wthr /sttime /fix /flip\n");
printf("Commands (type 2): /heal\n");
printf("Commands: /credits /cmds\n");
printf("-----------------------------------------------\n");

return 1;
}
uint8_t fn_OnPlayerMessage(int32_t playerId, const char* message)
{
char* name = (char*)malloc(256);
if(name)
{
vcmpError e=Server->GetPlayerName(playerId, name, 256);
if (e==0)
{
printf("[%u] %s: %s\n", playerId, name, message);
}
free(name);
}
return 1;
}
void fn_OnPlayerConnect (int32_t playerId)
{
/* Credits: zishan, stackoverflow for time function*/
time_t mytime = time(NULL);
char* time_str = ctime(&mytime);
time_str[strlen(time_str) - 1] = '\0';

char* name = (char*)malloc(256);
if(name)
{
vcmpError e = Server->GetPlayerName(playerId, name, 256);
if (e == 0)
{
char buffer[256];
int n = sprintf(buffer, "[%s] [%u] [%s] connected.", time_str, playerId, name);
if (n > 0)
{
Server->LogMessage(buffer);
}
}
free(name);
}
}
void fn_OnPlayerDisconnect (int32_t playerId, vcmpDisconnectReason reason)
{
/* Credits: zishan, stackoverflow for time function */
time_t mytime = time(NULL);
char* time_str = ctime(&mytime);
time_str[strlen(time_str) - 1] = '\0';

char buffer[256];
int n=sprintf(buffer, "[%s] [%u] disconnected.", time_str, playerId);
if (n > 0)
{
Server->LogMessage(buffer);
}
}

uint8_t fn_OnPlayerCommand(int32_t playerId, const char* message)
{
int idx=0;
char* text=(char*)malloc(strlen(message)+1);
if(text)
{
strcpy(text, message);
char* cmd;
cmd=strtok(text, " ");
if(!cmd)return 1;
if(strcmp(cmd, "credits")==0)
{
Server->SendClientMessage(playerId,0xFFFFFFFF,"Credits: habi, Banaqs (2014) for 200 vehicles");
}else if(strcmp(cmd, "cmds")==0)
{
Server->SendClientMessage(playerId, 0xFFFFFFFF, "/veh /wthr /sttime /fix /flip /heal /credits");
}else if(strcmp(cmd, "fix")==0)
{
int vehicleId=Server->GetPlayerVehicleId(playerId);
if(vehicleId)
{
Server->SetVehicleHealth(vehicleId, 1000.0);
Server->SetVehicleDamageData(vehicleId, 0);
Server->SendClientMessage(playerId, 0xFFFFFFFF, "Fixed");
}else printf("You must be in a vehicle to use this command.\n");
}else if(strcmp(cmd, "flip")==0)
{
int vehicleId=Server->GetPlayerVehicleId(playerId);
if(vehicleId)
{
float x, y, z;
Server->GetVehiclePosition(vehicleId, &x, &y, &z);
Server->SetVehiclePosition(vehicleId, x, y, z, 0);
Server->SendClientMessage(playerId, 0xFFFFFFFF, "Flipped");
}else printf("You must be in a vehicle to use this command.\n");
}else if(strcmp(cmd, "wthr")==0)
{
char* arg=strtok(NULL," ");
char* pEnd;
if(arg)
{
int id=(int)strtol(arg,&pEnd, 0);
if(id || id==0 &&strcmp(pEnd,"")==0)
{
Server->SetWeather(id);
for(int i=0;i<Server->GetMaxPlayers();i++)
{
if(i==playerId)continue;
if(Server->IsPlayerConnected(i))
{
if(Server->IsPlayerSpawned(i))
{
Server->SendClientMessage(i, 0xFFFFFFFF,"Weather changed!");
}
}
}
}
}else Server->SendClientMessage(playerId, 0xFFFFFFFF, "Use: /wthr id");
}else if(strcmp(cmd, "sttime")==0)
{
char* hour=strtok(NULL, " ");
char* min=strtok(NULL, " ");
if(hour && min)
{
char* pEnd;int s=0;
int ihour=(int)strtol(hour,&pEnd, 0);
if(ihour || strcmp(pEnd, "")==0)
{
int imin=(int)strtol(min, &pEnd, 0);
if(imin || strcmp(pEnd,"")==0)
{
Server->SetHour(ihour);
Server->SetMinute(imin);
s=1;
}
}
if(!s)Server->SendClientMessage(playerId, 0xFFFFFFFF, "Error, use /settime hr min");
}else Server->SendClientMessage(playerId, 0xFFFFFFFF, "Use: /sttime hr min");
}else if(strcmp(cmd, "heal")==0)
{
if(!Server->IsPlayerSpawned(playerId))
return 1;
float x, y, z;
Server->GetPlayerPosition(playerId, &x, &y, &z);
float p, q, r;
int closestplayerId=-1;float smallestdistance=2;
for(int i=0;i<Server->GetMaxPlayers();i++)
{
if(i==playerId)
continue;
if(Server->IsPlayerConnected(i))
{
if(Server->IsPlayerSpawned(i))
{
Server->GetPlayerPosition(i,&p,&q,&r);
float distance=sqrt(pow(x-p,2)+pow(y-q,2)+pow(z-r,2));
if(distance<smallestdistance)
{
closestplayerId=i;
smallestdistance=distance;
}
}
}
}
if(closestplayerId!=-1)
{
if(Server->GetPlayerHealth(closestplayerId)<100)
{
Server->SetPlayerHealth(closestplayerId, 100.0);
Server->SendClientMessage(closestplayerId, 0xFFFFFFFF, "You have been healed.");
}
}else
Server->SendClientMessage(playerId, 0xFFFFFFFF,"You must be near a player to heal him/her.");
}else if(strcmp(cmd, "veh")==0)
{
if(!Server->IsPlayerSpawned(playerId))
return 1;
char* arg=strtok(NULL," ");
if(arg)
{
char* pEnd;
short wVehicleId=(int)strtol(arg,&pEnd, 0);
if(wVehicleId)
{
if(Server->CheckEntityExists(vcmpEntityPoolVehicle, wVehicleId))
{
int iSomePlayerId=Server->GetVehicleOccupant(wVehicleId, 0);
if(iSomePlayerId==255)
{
float x,y,z;
Server->GetPlayerPosition(playerId, &x, &y, &z);
Server->SetVehiclePosition(wVehicleId, x, y, z,0);
Server->PutPlayerInVehicle(playerId,wVehicleId, 0, 0,1);
}
return 1;
}
}
Server->SendClientMessage(playerId, 0xFFFFFFFF, "Error: Invalid vehicle id");
}else Server->SendClientMessage(playerId, 0xFFFFFFFF, "Usage: /veh id");
}
free(text);
}
return 1;
}
unsigned int VcmpPluginInit(PluginFuncs* pluginFuncs, PluginCallbacks* pluginCallbacks, PluginInfo* pluginInfo) {
if(!pluginFuncs)return 1;//Precaution

Server=pluginFuncs;
// Plugin information
pluginInfo->pluginVersion = 0x1;
pluginInfo->apiMajorVersion = PLUGIN_API_MAJOR;
pluginInfo->apiMinorVersion = PLUGIN_API_MINOR;

pluginCallbacks->OnServerInitialise = fn_OnServerInitialise;
pluginCallbacks->OnPlayerMessage = fn_OnPlayerMessage;
pluginCallbacks->OnPlayerConnect = fn_OnPlayerConnect;
pluginCallbacks->OnPlayerDisconnect = fn_OnPlayerDisconnect;
pluginCallbacks->OnPlayerCommand=fn_OnPlayerCommand;
return 1;
}
[/noae][/noae][/noae][/noae][/noae][/noae][/noae]

habi

PluginAPI.h
This is the same as the plugin.h the api for creating plugins for vc-mp server. Available on the following link
https://bitbucket.org/stormeus/0.4-squirrel/src/master/VCMP.h

Structure of directory folder
-----------------------------
-plugins (folder)
-source
   |
   ----PluginAPI.h
   ----vcmpsrvr.c
-server32.exe ( or other versions according to 32 /64 bit  Win/Linux )
-server.cfg

server.cfg
plugins vcmpsrvr86
Instructions
Creating the Plugin
===================
On Windows
----------
Open command prompt:
cd source
gcc -m32 -shared vcmpsrvr.c -o "../plugins/vcmpsrvr86.dll"
( Omit '-m32' to build 64 bit dll.
note to change name vcmpsrvr86 also )
This will create 'vcmpsrvr86.dll' in 'plugins' folder
On Linux
--------
Navigate to following directory
cd source
gcc -shared vcmpsrvr.c -o "../plugins/vcmpsrvr64.so" -fpic

( Use -m32 to compile for x86 machines.)
This will create 'vcmpsrvr64.so' in 'plugins' folder

If you get error gcc is not recognized as an internal or external command, you need to install gcc

I recommend using Visual Studio Code or Visual Studio Community or Notepad++ to code the files.
Using the first two, you get suggestions ( all functions like SetPlayerPosition , etc ). Using Notepad++ you get good highlighting.

Download Plugin(    570.3 KB  (22.Oct.2022) - contains all configured windows server plus source files )

Download Plugin for Linux ( Sourceforge.net 376.9 KB 29.Jul.2022 ) ( VCMP Server Version v34 with 64 bit plugin (.so) and source files )

Screenshot

AdTec_224

First off I appreciate your enthusiasm for helping to keep VC:MP alive, however there are a few problems with example (bare in mind I've only had a quick look).

  • The first problem I see is you're never checking "Server" is a valid pointer anywhere. While yes, it should always be valid, never assume anything. Always check before using it.
  • for (int i = 1; i < 3000; i++) should be 1000 not 3000 as the vehicle limit is 1000 and you're just wasting CPU cycles.
  • fn_OnPlayerMessage and fn_OnPlayerConnect both have the same mistake, if for whatever reason GetPlayerName returns something other than 0, you're not freeing memory causing a memory leak.
  • fn_OnPlayerCommand allocates memory but doesn't free memory AT ALL, that'll cause a memory leak everytime a player issues a command, which is a big mistake.

For the above reasons I highly recommend avoiding using this, however I won't delete the topic as it gives yourself and other people the opportunity to learn.
It's also a really good demonstration as to why we as developers give server owners scripting languages such as Squirrel that offer garbage collection rather than expecting people to use languages like C.

habi

Thank you for the advice.

the bugs are now fixed.

habi

How is everybody doing..

Added Tommy Golf Skin and Tommy Cuban Skin.

Tommy Golf Skin has Baseball bat, Python and a Snipper.

Tommy Cuban has Grenade, Shotgun and Katana. :)

Sebastian

Quote from: habi on Oct 22, 2022, 05:12 PMHow is everybody doing..

Much part of the action is on Discord. Join there and you will meet all of us! :D
Welcome back!