ASI plugin for GTA San Andreas + SAMP — intercepts object packets from a server in real-time and exports them to a ready-to-use
.pwnfile.
The plugin is loaded automatically by ASI Loader when GTA SA starts. It hooks IncomingPacket inside samp.dll using MinHook, intercepting every CreateObject, SetMaterial, SetMaterialText, and RemoveBuilding packet sent by the server and storing them in memory. When recording is stopped, all data is flushed to a .pwn file.
Server SAMP
│ RakNet UDP
▼
samp.dll → IncomingPacket()
│ ← hooked here
▼
HookedIncomingPacket()
├── 0x5A CreateObject → store in g_objects[]
├── 0x5C DestroyObject → remove from g_stream[]
├── 0x7A SetMaterial → append to object entry
├── 0x7B SetMaterialText → append to object entry
└── 0x7C RemoveBuilding → store in g_rbTemp[]
| Software | Version |
|---|---|
| Visual Studio | 2019 / 2022 |
| GTA San Andreas | 1.0 US (downgraded) |
| SAMP | 0.3.7 R1 or R2 |
| ASI Loader | latest |
| MinHook | 1.3.3 |
Required Visual Studio components:
- Desktop development with C++
- Windows SDK (any version)
- MSVC v143 or v142 build tools
club_mapstealer/
├── club_mapstealer.cpp # main source
├── club_mapstealer.def # module definition (export)
├── club_mapstealer.vcxproj # Visual Studio project
├── include/
│ └── MinHook.h
└── lib/
└── MinHook.x86.lib
Download MinHook_133_bin.zip and copy:
MinHook.h → include/MinHook.h
MinHook.x86.lib → lib/MinHook.x86.lib
Project → Properties → Linker → Input → Additional Dependencies
Add: lib\MinHook.x86.lib
Inside MainThread, hook samp.dll:
#include "MinHook.h"
typedef bool (__cdecl* IncomingPacket_t)(void* rakPeer, unsigned char* data, int length, const char* ip);
IncomingPacket_t OrigIncomingPacket = nullptr;
bool __cdecl HookedIncomingPacket(void* rakPeer, unsigned char* data, int length, const char* ip)
{
if (length > 0) {
switch (data[0]) {
case 0x5A: ParseCreateObject(data, length); break;
case 0x5C: ParseDestroyObject(data, length); break;
case 0x7A: ParseSetMaterial(data, length); break;
case 0x7B: ParseSetMatText(data, length); break;
case 0x7C: ParseRemoveBuilding(data, length); break;
}
}
return OrigIncomingPacket(rakPeer, data, length, ip);
}
// In MainThread:
HMODULE hSamp = GetModuleHandleA("samp.dll");
DWORD base = (DWORD)hSamp;
MH_Initialize();
MH_CreateHook((LPVOID)(base + 0xXXXXX), &HookedIncomingPacket, (LPVOID*)&OrigIncomingPacket);
MH_EnableHook(MH_ALL_HOOKS);Note: The offset
0xXXXXXdiffers per SAMP version. Find it using ReClass / x32dbg or refer to sa-mp-reverse.
typedef void(__cdecl* ClientCmd_t)(const char* cmd);
ClientCmd_t OrigClientCmd;
void __cdecl HookedClientCmd(const char* cmd) {
if (!HandleCommand(cmd))
OrigClientCmd(cmd);
}Set configuration to Release | Win32, then:
Ctrl+Shift+B
Output: Release/club_mapstealer.asi
Alternatively with MinGW:
g++ -m32 -shared -o club_mapstealer.asi club_mapstealer.cpp \
-static -lstdc++ -lkernel32 -luser32- Make sure ASI Loader is installed in your GTA SA folder (
dinput8.dllorvorbisFile.dll) - Copy
club_mapstealer.asito the root GTA SA folder - Launch GTA SA and join a SAMP server
- Type
/maphelpin chat to confirm the plugin is active
Output is saved to: <GTA SA folder>\cmap\
1. Join the server whose map you want to record
2. Move to the target area
(the plugin only captures objects within g_maxDist radius)
3. Optional: extend the capture range
/setrendermap 300
4. Start recording
/maprecord
5. Walk around the server area
SAMP only streams objects close to the player's position
6. Stop and save
/maprecord (or wait for autosave every 100 objects)
7. Open the .pwn file at: GTA SA folder\cmap\filename.pwn
Tips:
- Use
/flymodeto cover the area faster - Use
/saveslot 1before moving to a new area as a backup - Use
/nearobj 50to check which objects have been captured
| Command | Arguments | Description |
|---|---|---|
/maphelp |
— | Show all available commands |
/maprecord |
— | Toggle recording; auto-saves on stop |
/flymode |
— | Toggle fly mode (no gravity) |
/savemap |
[name] |
Save to file (default: timestamp) |
/clearmap |
— | Clear all recorded objects |
/mapinfo |
— | Stats: object count, materials, rb, slots |
/nearobj |
[radius] |
List objects within radius (default 20 units) |
/delobj |
<id> |
Delete an object by internal ID |
/preview |
<id> |
Show detailed info for one object |
/saveslot |
<1-5> |
Save current state to a memory slot |
/loadslot |
<1-5> |
Load state from a slot |
/mergeslot |
<src> <dst> |
Merge two slots |
/showtext3d |
— | Toggle 3D labels above objects |
/setrendermap |
<dist> |
Set object detection range (1–1000) |
/setmaxobj |
<n> |
Maximum object limit (0 = unlimited) |
/mapsound |
— | Toggle sound when an object is captured |
/rescan |
— | Re-scan existing GTA SA objects |
// Club Map Stealer | dist: 200
// Objects: 42 | Materials: 10 | Text: 3 | RemoveBuildings: 5
public OnGamemodeInit()
{
new cmap;
cmap = CreateDynamicObject(1337, 100.0000, 200.0000, 10.0000, 0.0000, 0.0000, 0.0000, -1, -1, -1, 200.0, 200.0);
SetDynamicObjectMaterial(cmap, 0, 1337, "matname", "texname", 0);
}
public OnPlayerConnect(playerid)
{
RemoveBuildingForPlayer(playerid, 700, 100.000, 200.000, 10.000, 0.250);
}Plugin doesn't load
Make sure ASI Loader is installed — check for dinput8.dll or another loader file in your GTA SA folder.
Commands don't respond
The chat command hook isn't fully implemented. Add a hook to SendChat or WndProc inside MainThread.
Empty .pwn file
The packet hook isn't active. Make sure the IncomingPacket offset is correct for your SAMP version.
Objects not being captured
Verify g_recording = true (type /maprecord) and that your player position is within MAX_DIST.
SAMP 0.3DL Offsets differ from 0.3.7. Use an offset finder tool for the correct version.
- SAMP-API — SAMP internals
- MinHook — hooking library
- plugin-sdk — GTA SA plugin SDK (optional)
- sa-mp-reverse — SAMP packet offsets