This is primarily for clans to show their tactics or setting up bots to do movements, they'd never do themselves.
Imagine you're recording yourself jumping past the double doors at dust2 several times and spawn some CT bots doing the same. You're able to improve your awp skills as T now, trying to hit those fast jumpers through the double doors. sega74rus created a video (using his plugin).
It's pretty awesome to see a bot bunnyhopping.. (or this)
Details The recordings are saved per map in sourcemod/data/botmimic/%CATEGORY%/map_name/%SUBDIR%/%TIMESTAMP%.rec
The subdirectory can be omitted, the default category is "default". The menu plugin doesn't use any subdirectories, they are to be used by other plugins recording players using the provided natives to organize their data.
Usage
All you have to do is type /mimic in chat to open the menu and start to record. All* movements you're performing from now on are stored. When you're done, use the menu or type /stoprecord to stop recording. You can also pause and resume recording if you need a short break.
Now you're able to select an existing bot or add a new bot to mimic your recorded movements.
To organize your records, you can create categories and record replays into them. If you don't choose a category from the menu, they'll be stored in the "default" category.
You can save bookmarks while recording (sm_savebookmark) and jump to them while a player is playing back the record easily through the menu.
* silencers, weapon firemodes and reloadings aren't saved yet. Also health, armor and ammo aren't, since it's primarily for movement capturing! Bots are given a new weapon with default ammo when switching to it the first time during record.
The botmimic core plugin doesn't do anything by itself, but provides the interface for other plugins to manage replays and handles the recording logic.
The botmimic_menu plugin provides the admin menu integration and admin commands described above.
The botmimic_training plugin is an example to add visual output when hitting replaying bots.
Optional DHooks dependency
Bot Mimic uses DHooks to hook Teleport on clients. This allows for recording any teleportation triggered by the map or any other way, which is useful for e.g. bhop maps, where you're teleported somewhere, when you hit the ground.
To avoid strange behaviour I really recommend to install DHooks, it's not required though.
Commands
As already mentioned there are 2 admin commands in the botmimic_menu plugin (config flag):
sm_mimic - Opens the bot mimic menu
sm_stoprecord - Stops your current record
sm_savebookmark - Saves a bookmark with the given name in the record the target records. Usage: sm_savebookmark <name|steamid|#userid> <bookmark name>
ConVars
sm_botmimic_version - ...
sm_botmimic_snapshotinterval - Save the position of clients every x ticks. This is to avoid bots getting stuck in walls during a long playback and lots of jumps. (Default: 10000)
sm_botmimic_respawnondeath - Respawn the bot when he dies during playback? (Default: 1)
botmimic_training plugin:
sm_botmimic_showdamage - Show damage and hitgroup when hitting a mimicing bot? (Default: 1)
sm_botmimic_playhitsound - Play a sound when hitting a mimicing bot? (Default: 1)
enum BMError { BM_NoError = 0, BM_BadClient, // Provided client index is invalid BM_FileNotFound, // The file does not exists or can't be opened BM_BadFile, // Not a Bot Mimic record. BM_NewerBinaryVersion // The record was recorded with a newer version of Bot Mimic and can't be played. }
/** * Start to record the movements of a player. * It's stored in memory until told to write to disk. * * Make sure the player isn't recording already. * * @param client The client to record * @param name The name of the record (anything, not the filename) * @param category The category to put this record in. This is used as a directory filename inside /data/botmimic/%CATEGORY%/%MAP_NAME%/stuff.rec * @param subdir The subdirectory to organize records physically. This isn't used or retrieved back anywhere, just to better manage files by hand. /data/botmimic/%CATEGORY%/%MAP_NAME%/%SUBDIR%/stuff.rec * @noreturn */ native BotMimic_StartRecording(client, const String:name[], const String:category[]="", const String:subdir[]="");
/** * Pause recording the current record. * * @param client The recording client, which should pause recording. * @noreturn * @error Invalid client index, client not recording or recording already paused. */ native BotMimic_PauseRecording(client);
/** * Resume recording the current record. * * @param client The recording client, which should resume recording. * @noreturn * @error Invalid client index, client not recording or record not paused. */ native BotMimic_ResumeRecording(client);
/** * Check whether a client is currently paused recording a record. * * @param client The client index of the player you want to know if he's paused. * @return True if recording is paused, false otherwise. * @error Invalid client index or client not recording. */ native bool:BotMimic_IsRecordingPaused(client);
/** * Stop recording and save or discard the record * * @param client The client which was recording * @param save Save the record (true) or discard (false) * @noreturn */ native BotMimic_StopRecording(client, bool:save=true);
/** * Save the current frame with the given name as bookmark. * You can skip to this point when playing back the record using BotMimic_GoToBookmark. * * @param client The client which is recording * @param name The name of this bookmark * @noreturn * @error Client not recording */ native BotMimic_SaveBookmark(client, const String:name[]);
/** * Deletes a record and does some checks, that the file is really a record. * * @param path The Path to the .rec recording file to delete. * @return Number of bots, which were mimicing this record or -1 on error (file not found/invalid) */ native BotMimic_DeleteRecord(const String:path[]);
/** * Is the player currently recording something? * * @param client The client to check * @return true, if player is recording, false otherwise */ native bool:BotMimic_IsPlayerRecording(client);
/** * Is the player currently mimicing something? * * @param client The client to check * @return true, if the player is mimicing, false otherwise */ native bool:BotMimic_IsPlayerMimicing(client);
/** * Get the path to the record file this player is currently mimicing. * * @param client The mimicing client * @param path A String where to store the path * @param maxlen The maximal length of the path * @noreturn */ native BotMimic_GetRecordPlayerMimics(client, String:path[], maxlen);
/** * Let a client start to mimic a record from a file. * * @param client The client, which should start mimicing * @param path The path to a .rec recording file * @return BM_NoError if all went smooth, see BMError definition. */ native BMError:BotMimic_PlayRecordFromFile(client, const String:path[]);
/** * Let a client start to mimic a record by providing the record name. * Since record names don't have to be unique, this native picks the most recent one. * * @param client The client, which should start mimicing. * @param name The name of the record to play. * @return BM_NoError if all went smooth, see BMError definition. */ native BMError:BotMimic_PlayRecordByName(client, const String:name[]);
/** * Have a bot restart the record he's currently playing from the beginning. * * @param client The client, who should start over with the record he's currently mimicing. * @noreturn */ native BotMimic_ResetPlayback(client);
/** * Jump the the frame where this bookmark was set in the record. * @see BotMimic_SaveBookmark * * @param client The client, which is mimicing some record * @param name The name of the bookmark saved during recording using BotMimic_SaveBookmark * @noreturn */ native BotMimic_GoToBookmark(client, const String:name[]);
/** * Stops a player from mimicing a record. * * @param client The client who should stop mimicing * @noreturn */ native BotMimic_StopPlayerMimic(client);
/** * Get the Handle to a sorted adt_array, containing a list of paths to all loaded record files. * DON'T CLOSE THIS HANDLE! * Just use GetArrayString to read the path. * * @return Handle to sorted adt_array containing all paths to records */ native Handle:BotMimic_GetLoadedRecordList();
/** * Get the Handle to a sorted adt_array, containing a list of categorys. * DON'T CLOSE THIS HANDLE! * Just use GetArrayString to read the category. * * @return Handle to sorted adt_array containing all categories. */ native Handle:BotMimic_GetLoadedRecordCategoryList();
/** * Get the header information of a record. This includes the record name. * * @param path The path to the .rec record file * @param iFileHeader An array to store the file header in. * @return BM_NoError if all went smooth, see BMError definition. */ native BMError:BotMimic_GetFileHeaders(const String:path[], iFileHeader[BMFileHeader], size=_:BMFileHeader);
/** * Get the category of a record. * * @param path The path to the .rec record file. * @param category The String where to store the category in. * @param maxlen The maximal length of the category buffer. * @return true if category of file found, false otherwise */ native bool:BotMimic_GetFileCategory(const String:path[], String:category[], maxlen);
/** * Change the name of a record. This doesn't change the filename, but the stored record name inside the file. (BMTP_recordName) * * @param path The path to the .rec record file which name to change. * @param name The new name of the record * @return BM_NoError if all went smooth, see BMError definition. */ native BMError:BotMimic_ChangeRecordName(const String:path[], String:name[]);
/** * Get a list of all bookmarks stored in this record as an adt_array. * You have to close the returned Handle using CloseHandle. * * @param path The path to the .rec record file. * @param bookmarks The handle where the bookmark array will be stored in. * @return BM_NoError if all went smooth, see BMError definition. */ native BMError:BotMimic_GetRecordBookmarks(const String:path[], &Handle:bookmarks);
Forwards
PHP Code:
/** * Called when a player starts recording. * path is NOT the path to the .rec file, but just the path to the category folder (by default: Path_SM/data/botmimic/%CATEGORY%) * * @param client The client index, who's being recorded * @param name The name of the recording (stored in file header) * @param category The category name * @param subdir The subdir used to organize the record files (no further sense..) * @param path The path to the category folder * @return >= Plugin_Handled to abort recording or Plugin_Continue to let it pass and start recording. */ forward Action:BotMimic_OnStartRecording(client, String:name[], String:category[], String:subdir[], String:path[]);
/** * Called when recording a record is paused or resumed. * * @param client The client which recording pause state changed * @param paused True when the recording was paused, false when it was unpaused * @noreturn */ forward BotMimic_OnRecordingPauseStateChanged(client, bool:paused);
/** * Called when a bookmark is saved while the client is recording. * * @param client The client which is recording. * @param bookmark The name of the saved bookmark. * @noreturn */ forward BotMimic_OnRecordingBookmarkSaved(client, const String:bookmark[]);
/** * Called when a player stops recording. * Change the save param if you want to prevent it from being discarded/written. * * @param client The client who was recording. * @param name The name of the record (stored in file header) * @param category The category name. * @param subdir The subdir used to organize the record files (no further sense..) * @param path The path to the category folder * @param save Set to true, if the record should be stored to disk, or false to discard. * @return >= Plugin_Handled to continue recording, Plugin_Continue to let it stop. */ forward Action:BotMimic_OnStopRecording(client, String:name[], String:category[], String:subdir[], String:path[], &bool:save);
/** * Called when a record was saved to file. * * @param client The client who was recording. * @param name The name of the record (stored in file header) * @param category The category name. * @param subdir The subdir used to organize the record files (no further sense..) * @param file The actual path to the saved .rec file. * @noreturn */ forward BotMimic_OnRecordSaved(client, String:name[], String:category[], String:subdir[], String:file[]);
/** * Called when a record was deleted. * * @param name The name of the record (stored in file header) * @param category The category of the file. (If it wasn't loaded before it defaults to "default".) * @param path The path to the deleted file. * @noreturn; */ forward BotMimic_OnRecordDeleted(String:name[], String:category[], String:path[]);
/** * Called when a player starts to mimic a record * * @param client The client which starts mimicing. * @param name The name of the record (stored in file header) * @param category The category of the file. * @param path The path to the record file. * @return >= Plugin_Handled to stop the player from mimicing, Plugin_Continue to allow */ forward Action:BotMimic_OnPlayerStartsMimicing(client, String:name[], String:category[], String:path[]);
/** * Called when a player stops mimicing a record. * * @param client The client who was mimicing * @param name The name of the record (stored in file header) * @param category The category of the file. * @param path The path to the record file. * @noreturn */ forward BotMimic_OnPlayerStopsMimicing(client, String:name[], String:category[], String:path[]);
/** * Called everytime a mimicing player starts to mimic the record again from the beginning, * when he was teleported back to the start. * * @param client The mimicing client * @noreturn */ forward BotMimic_OnPlayerMimicLoops(client);
/** * Called when a bookmark is reached during playback of a record. * * @param client The client which is mimicing. * @param bookmark The name of the bookmark. * @noreturn */ forward BotMimic_OnPlayerMimicBookmark(client, const String:bookmark[]);
Requirements
To run the plugin:
SDK Hooks (old thread, included in SourceMod 1.5+)
Installation
Just drag&drop the botmimic.smx and botmimic_menu.smx into the plugins folder of your sourcemod install. If you want to use the training features, copy the botmimic_training.smx too.
Known issues
Bots get out of sync with the original movement when the recording player did a lot of duck-jumps. The bot will jump a little bit lower which might cause him to get stuck on edges and just randomly replay movement in the wrong place of the map. Try setting sm_botmimic_snapshotinterval to something lower like 1000 or 500. This won't fix broken already recorded replays, but help recover in future recordings.
You can use this script to debug the record files.
Changelog
Spoiler
Quote:
2.0 (22.07.2013): Initial release of rewrite
2.0.1 (01.08.2013):
Actually made DHooks an optional dependency
2.1 (02.10.2014):
Fixed crash and problems with grenades in CS:GO
Added bookmarks
Save bookmarks while recording and jump to them while playing back
forward to monitor when a bookmark was reached
Added pause and resume option during recording
Players can pause recording and resume whenever they like. They are teleported to the new position on resume, if they moved while recording was paused.
Added sm_botmimic_snapshotinterval convar to dynamically set the amount of ticks when the absolute position of the player should be saved as a reference value, to avoid having the playing back player get too much out of sync.
Added sm_botmimic_respawnondeath convar to control if bots should be respawned if they are killed during playback.
Fixed case of showing wrong menu when dismissing a recording.
The plugin was entirely rewritten in version 2.0. Demos recorded with Bot Mimic 1 aren't compatible with the new version! The plugin is using a binary file format now instead of an ascii based one. This reduces file size drastically!
There is currently no way to convert old records to the new format.
Bunny hopping is easy (apparently) All you do is bounce around the game like a twat and talk shit & make stupid noises like a prat until everyone votebans you.
Would be cool if we could rename the records. Also, if you added a option where if X players are on the server it spawns bot doing X trick (multi bot doing different tricks could be added). That way players could practice killing pre-motion bots while they wait for more players.
I had the same idea about 2 years ago, also wanted to record entities and other stuff for machinimas, then throw some nonlinear editing gui, link it to server and we got a basic clone of Source Filmmaker done.