├── LICENSE ├── README.md ├── object-streamer-client ├── async-models.mjs ├── client.mjs ├── object-streamer.mjs └── resource.cfg └── server-scripts ├── ExampleServer.cs └── ObjectStreamer.cs /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Niels Visbeek 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED! USE THIS: https://github.com/CoffeeGen/altv-entity-streamers-os 2 | 3 | # ALT:V MP Server-side Object Streamer 4 | A server-side C# implementation of an object streamer for ALT:V MP. 5 | 6 | ## Changelog 7 | - Fixed some issues with rotation not setting correctly. 8 | - Some optimizations. 9 | - Added ``GetClosestDynamicObject``. 10 | - Fixed an issue with objects not reloading properly when they streamed out but weren't removed from cache. 11 | 12 | ## Installation 13 | - This resource makes use of the ``AltV.Net.EntitySync (v1.0.20-dev-preview)`` and ``AltV.Net.EntitySync.ServerEvent (v1.0.19-dev-preview)`` nuget package, make sure to install those prior to using this resource. 14 | - Copy ``server-scripts/ObjectStreamer.cs`` to your gamemode. 15 | - Make sure to add the following code to your gamemode's OnStart() method(the object streamer won't work without it!): 16 | ```csharp 17 | // Documentation: https://fabianterhorst.github.io/coreclr-module/articles/entity-sync.html 18 | AltEntitySync.Init( 1, 100, 19 | repository => new ServerEventNetworkLayer( repository ), 20 | ( ) => new LimitedGrid3( 50_000, 50_000, 100, 10_000, 10_000, 600 ), 21 | new IdProvider( ) 22 | ); 23 | ``` 24 | - Copy ``object-streamer-client`` to your ``server-root/resources`` directory. 25 | - Add ``"object-streamer-client"`` to your server config resources list. 26 | 27 | ## Usage 28 | The following global methods are available: 29 | ```csharp 30 | // Create a new object on the map, returns the created object. 31 | DynamicObject CreateDynamicObject( 32 | string model, Vector3 position, Vector3 rotation, int dimension = 0, bool? isDynamic = null, bool? frozen = null, uint? lodDistance = null, 33 | Rgb lightColor = null, bool? onFire = null, TextureVariation? textureVariation = null, bool? visible = null, uint streamRange = 400 34 | ); 35 | 36 | // Destroy an object by it's ID or object instance. returns true if successful. 37 | bool DestroyDynamicObject( ulong dynamicObjectId ); 38 | void DestroyDynamicObject( DynamicObject obj ); 39 | 40 | // Get an object by it's ID. returns the object if successful or null if not. 41 | DynamicObject GetDynamicObject( ulong dynamicObjectId ); 42 | 43 | // Get the closest dynamic object to the specified Vector3 position. 44 | (DynamicObject obj, float distance) GetClosestDynamicObject( Vector3 pos ); 45 | 46 | // Destroy all created objects. 47 | void DestroyAllDynamicObjects( ); 48 | 49 | // Get a list of all created objects. 50 | List GetAllDynamicObjects( ); 51 | ``` 52 | 53 | Each object has it's own set of methods that can be used to change properties: 54 | ```csharp 55 | // Get/set object's rotation 56 | Vector3 Rotation { get; set; } 57 | 58 | // Get/set object's position 59 | Vector3 Position { get; set; } 60 | 61 | // Get/set object's model 62 | string Model { get; set; } 63 | 64 | // Get/set LOD Distance (default null) 65 | uint? LodDistance { get; set; } 66 | 67 | // Get/set object's texture variation (default null) 68 | TextureVariation? TextureVariation { get; set; } 69 | 70 | // Get/set object's dynamic state (default null) 71 | bool? Dynamic { get; set; } 72 | 73 | // Get/set object's visibility state (default null) 74 | bool? Visible { get; set; } 75 | 76 | // Get/set object's on fire state (default null) (don't use this as of right now, it does create a fire but it's very small. requires further native testing). 77 | bool? OnFire { get; set; } 78 | 79 | // Get/set object's frozen state (default null) 80 | bool? Frozen { get; set; } 81 | 82 | // Get/set object's light color (default null) 83 | Rgb LightColor { get; set; } 84 | ``` 85 | 86 | ## Examples 87 | ```csharp 88 | // Create an object. 89 | DynamicObject obj = ObjectStreamer.CreateDynamicObject( "bkr_prop_biker_bblock_cor", new Vector3( -859.655f, -803.499f, 25.566f ), new Rotation( 0, 0, 0 ), 0 ); 90 | 91 | // Change object into a house. 92 | obj.Model = "lf_house_17_"; 93 | 94 | // Change position. 95 | obj.Position = new Position( 300f, 500f, 25f ); // Accepts both Vector3 and Position types. 96 | 97 | // Change rotation. 98 | obj.Rotation = new Rotation( 0f, 0f, 25f ); // Accepts both Vector3 and Rotation types. 99 | 100 | // Hide the object 101 | obj.Visible = false; 102 | 103 | // Set an object's texture variation 104 | obj.TextureVariation = TextureVariation.Nautical; 105 | 106 | // Set an object's light color 107 | obj.LightColor = new Rgb( 25, 49, 120 ); // random 108 | 109 | // Freeze an object 110 | obj.Frozen = true; 111 | 112 | // Destroy the object 113 | ObjectStreamer.DestroyDynamicObject( obj ); // has an overload method that accepts an ID instead of object instance. 114 | ``` 115 | 116 | Furthermore, there's an example C# file included in the package, the example file can be found at ``server-scripts/ExampleServer.cs``. 117 | 118 | ## Future plans 119 | - ``MoveDynamicObject( ulong objectId, Vector3 newPosition );`` 120 | - ``DestroyWorldPropAtCoords( string model, Vector3 position, float range )`` 121 | -------------------------------------------------------------------------------- /object-streamer-client/async-models.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | Developed by Micaww 3 | */ 4 | 5 | import * as alt from 'alt'; 6 | import * as natives from 'natives'; 7 | 8 | class AsyncModel { 9 | constructor( ) { 10 | this.loadingModels = new Set( ); 11 | } 12 | 13 | cancel( entityId ) { 14 | this.loadingModels.delete( +entityId ); 15 | } 16 | 17 | async load( entityId, model ) { 18 | return new Promise( resolve => { 19 | if( typeof model === 'string' ) 20 | model = natives.getHashKey( model ); 21 | 22 | this.loadingModels.add( +entityId ); 23 | 24 | natives.requestModel( model ); 25 | 26 | const interval = alt.setInterval( () => { 27 | if( !this.loadingModels.has( +entityId ) ) { 28 | return done( !!natives.hasModelLoaded( model ) ); 29 | } 30 | 31 | if( natives.hasModelLoaded( model ) ) { 32 | return done( true ); 33 | } 34 | }, 0 ); 35 | 36 | const timeout = alt.setTimeout( ( ) => { 37 | return done( !!natives.hasModelLoaded( model ) ); 38 | }, 3000 ); 39 | 40 | const done = result => { 41 | alt.clearInterval( interval ); 42 | alt.clearTimeout( timeout ); 43 | 44 | this.loadingModels.delete( +entityId ); 45 | resolve( result ); 46 | }; 47 | 48 | if( !natives.isModelValid( model ) ) 49 | return done( false ); 50 | 51 | if( natives.hasModelLoaded( model ) ) 52 | return done( true ); 53 | } ); 54 | } 55 | } 56 | 57 | export const asyncModel = new AsyncModel(); -------------------------------------------------------------------------------- /object-streamer-client/client.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | Developed by DasNiels/Niels/DingoDongBlueBalls 3 | Async Models loading by Micaww 4 | */ 5 | 6 | import * as alt from 'alt'; 7 | 8 | import { objStreamer } from "./object-streamer"; 9 | 10 | // when an object is streamed in 11 | alt.onServer( "entitySync:create", entity => { 12 | if( entity.data ) { 13 | let data = entity.data; 14 | 15 | if( data && data.entityType === "object" ) { 16 | 17 | objStreamer.addObject( 18 | +entity.id, data.model, data.entityType, 19 | entity.position, data.rotation, 20 | data.lodDistance, data.textureVariation, data.dynamic, 21 | data.visible, data.onFire, data.frozen, data.lightColor 22 | ); 23 | } 24 | } 25 | // this entity has streamed in before, fetch from cache 26 | else 27 | { 28 | objStreamer.restoreObject( +entity.id ); 29 | } 30 | } ); 31 | 32 | // when an object is streamed out 33 | alt.onServer( "entitySync:remove", entityId => { 34 | 35 | //alt.log( 'streamout: ', entityId ); 36 | objStreamer.removeObject( +entityId ); 37 | } ); 38 | 39 | // when a streamed in object changes position data 40 | alt.onServer( "entitySync:updatePosition", ( entityId, position ) => { 41 | let obj = objStreamer.getObject( +entityId ); 42 | 43 | if( obj === null ) 44 | return; 45 | 46 | objStreamer.setPosition( obj, position ); 47 | } ); 48 | 49 | // when a streamed in object changes data 50 | alt.onServer( "entitySync:updateData", ( entityId, newData ) => { 51 | let obj = objStreamer.getObject( +entityId ); 52 | 53 | if( obj === null ) 54 | return; 55 | 56 | if( newData.hasOwnProperty( "rotation" ) ) 57 | objStreamer.setRotation( obj, newData.rotation ); 58 | 59 | if( newData.hasOwnProperty( "model" ) ) 60 | objStreamer.setModel( obj, newData.model ); 61 | 62 | if( newData.hasOwnProperty( "lodDistance" ) ) 63 | objStreamer.setLodDistance( obj, newData.lodDistance ); 64 | 65 | if( newData.hasOwnProperty( "textureVariation" ) ) 66 | objStreamer.setTextureVariation( obj, newData.textureVariation ); 67 | 68 | if( newData.hasOwnProperty( "dynamic" ) ) 69 | objStreamer.setDynamic( obj, newData.dynamic ); 70 | 71 | if( newData.hasOwnProperty( "visible" ) ) 72 | objStreamer.setVisible( obj, newData.visible ); 73 | 74 | if( newData.hasOwnProperty( "onFire" ) ) 75 | objStreamer.setOnFire( obj, newData.onFire ); 76 | 77 | if( newData.hasOwnProperty( "frozen" ) ) 78 | objStreamer.setFrozen( obj, newData.frozen ); 79 | 80 | if( newData.hasOwnProperty( "lightColor" ) ) 81 | objStreamer.setLightColor( obj, newData.lightColor ); 82 | } ); 83 | 84 | // when a streamed in object needs to be removed 85 | alt.onServer( "entitySync:clearCache", entityId => { 86 | objStreamer.clearObject( +entityId ); 87 | } ); -------------------------------------------------------------------------------- /object-streamer-client/object-streamer.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | Developed by DasNiels/Niels/DingoDongBlueBalls 3 | */ 4 | 5 | import * as alt from 'alt'; 6 | import * as natives from 'natives'; 7 | 8 | import { asyncModel } from "./async-models"; 9 | 10 | class ObjectStreamer { 11 | constructor( ) { 12 | this.objects = []; 13 | } 14 | 15 | async addObject( entityId, model, entityType, pos, rot, lodDistance, textureVariation, dynamic, visible, onFire, frozen, lightColor ) { 16 | // clear the object incase it still exists. 17 | this.removeObject( entityId ); 18 | this.clearObject( entityId ); 19 | 20 | if( !await asyncModel.load( +entityId, model ) ) 21 | return alt.log( `[OBJECT-STREAMER] Couldn't create object with model ${ model }.` ); 22 | 23 | let handle = natives.createObjectNoOffset( natives.getHashKey( model ), pos.x, pos.y, pos.z, true, true, false ); 24 | 25 | let obj = { handle: handle, entityId: +entityId, model: model, entityType: entityType, position: pos }; 26 | this.objects.push( obj ); 27 | 28 | this.setRotation( obj, rot ); 29 | this.setLodDistance( obj, lodDistance ); 30 | this.setTextureVariation( obj, textureVariation ); 31 | this.setDynamic( obj, dynamic ); 32 | this.setVisible( obj, visible ); 33 | this.setOnFire( obj, onFire ); 34 | this.setFrozen( obj, frozen ); 35 | this.setLightColor( obj, lightColor ); 36 | } 37 | 38 | getObject( entityId ) { 39 | let obj = this.objects.find( o => +o.entityId === +entityId ); 40 | 41 | if( !obj ) 42 | return null; 43 | 44 | return obj; 45 | } 46 | 47 | async restoreObject( entityId ) { 48 | let obj = this.getObject( entityId ); 49 | 50 | if( obj === null ) 51 | return; 52 | 53 | if( !await asyncModel.load( +entityId, obj.model ) ) 54 | return alt.log( `[OBJECT-STREAMER] Couldn't create object with model ${ obj.model }.` ); 55 | 56 | obj.handle = natives.createObjectNoOffset( natives.getHashKey( obj.model ), obj.position.x, obj.position.y, obj.position.z, true, true, false ); 57 | 58 | this.setRotation( obj, obj.rotation ); 59 | this.setLodDistance( obj, obj.lodDistance ); 60 | this.setTextureVariation( obj, obj.textureVariation ); 61 | this.setDynamic( obj, obj.dynamic ); 62 | this.setVisible( obj, obj.visible ); 63 | this.setOnFire( obj, obj.onFire ); 64 | this.setFrozen( obj, obj.frozen ); 65 | this.setLightColor( obj, obj.lightColor ); 66 | } 67 | 68 | removeObject( entityId ) { 69 | let obj = this.getObject( entityId ); 70 | 71 | if( obj === null ) 72 | return; 73 | 74 | asyncModel.cancel( +entityId ); 75 | 76 | natives.deleteObject( +obj.handle ); 77 | obj.handle = null; 78 | } 79 | 80 | clearObject( entityId ) { 81 | let idx = this.objects.findIndex( o => +o.entityId === +entityId ); 82 | 83 | if( idx === -1 ) 84 | return; 85 | 86 | this.objects.splice( idx, 1 ); 87 | } 88 | 89 | setRotation( obj, rot ) { 90 | natives.setEntityRotation( +obj.handle, rot.x, rot.y, rot.z, 2, false ); 91 | obj.rotation = rot; 92 | } 93 | 94 | setPosition( obj, pos ) { 95 | natives.setEntityCoordsNoOffset( +obj.handle, pos.x, pos.y, pos.z, true, true, true ); 96 | obj.position = pos; 97 | } 98 | 99 | async setModel( obj, model ) { 100 | obj.model = model; 101 | } 102 | 103 | setLodDistance( obj, lodDistance ) { 104 | if( lodDistance === null ) 105 | return; 106 | 107 | natives.setEntityLodDist( +obj.handle, +lodDistance ); 108 | obj.lodDistance = lodDistance; 109 | } 110 | 111 | setTextureVariation( obj, textureVariation ) { 112 | if( textureVariation === null ) 113 | { 114 | if( obj.textureVariation !== null ) 115 | { 116 | natives.setObjectTextureVariation( +obj.handle, +textureVariation ); 117 | obj.textureVariation = null; 118 | } 119 | return; 120 | } 121 | 122 | natives.setObjectTextureVariation( +obj.handle, +textureVariation ); 123 | obj.textureVariation = textureVariation; 124 | } 125 | 126 | setDynamic( obj, dynamic ) { 127 | if( dynamic === null ) 128 | return; 129 | 130 | natives.setEntityDynamic( +obj.handle, !!dynamic ); 131 | obj.dynamic = !!dynamic; 132 | } 133 | 134 | setVisible( obj, visible ) { 135 | if( visible === null ) 136 | return; 137 | 138 | natives.setEntityVisible( +obj.handle, !!visible, false ); 139 | obj.textureVariation = !!visible; 140 | } 141 | 142 | setOnFire( obj, onFire ) { 143 | if( onFire === null ) 144 | return; 145 | 146 | if( !!onFire ) 147 | { 148 | obj.fireHandle = natives.startScriptFire( obj.position.x, obj.position.y, obj.position.z, 1, true ); 149 | } 150 | else 151 | { 152 | if( obj.fireHandle !== null ) 153 | { 154 | natives.removeScriptFire( +obj.fireHandle ); 155 | obj.fireHandle = null; 156 | } 157 | } 158 | 159 | obj.onFire = !!onFire; 160 | } 161 | 162 | setFrozen( obj, frozen ) { 163 | if( frozen === null ) 164 | return; 165 | 166 | natives.freezeEntityPosition( +obj.handle, !!frozen ); 167 | obj.frozen = !!frozen; 168 | } 169 | 170 | setLightColor( obj, lightColor ) { 171 | if( lightColor === null ) 172 | natives.setObjectLightColor( +obj.handle, false, 0, 0, 0 ); 173 | else 174 | natives.setObjectLightColor( +obj.handle, true, +lightColor.r, +lightColor.g, +lightColor.b ); 175 | 176 | obj.lightColor = lightColor; 177 | } 178 | } 179 | 180 | export const objStreamer = new ObjectStreamer(); 181 | 182 | alt.on( "resourceStop", ( ) => { 183 | objStreamer.objects.forEach( ( obj ) => { 184 | objStreamer.removeObject( +obj.entityId ); 185 | objStreamer.clearObject( +obj.entityId ); 186 | } ); 187 | } ); -------------------------------------------------------------------------------- /object-streamer-client/resource.cfg: -------------------------------------------------------------------------------- 1 | type: js 2 | client-main: client.mjs 3 | 4 | client-files: [ 5 | client.mjs, 6 | object-streamer.mjs, 7 | async-models.mjs 8 | ] -------------------------------------------------------------------------------- /server-scripts/ExampleServer.cs: -------------------------------------------------------------------------------- 1 | using AltV.Net; 2 | using AltV.Net.Async; 3 | using AltV.Net.Data; 4 | using AltV.Net.Elements.Entities; 5 | using AltV.Net.EntitySync; 6 | using AltV.Net.EntitySync.ServerEvent; 7 | using AltV.Net.EntitySync.SpatialPartitions; 8 | using DasNiels.AltV.Streamers; 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Numerics; 13 | using System.Threading.Tasks; 14 | 15 | namespace TestServer 16 | { 17 | public class ExampleServer : AsyncResource 18 | { 19 | public override void OnStart( ) 20 | { 21 | // YOU MUST ADD THIS IN THE ONSTART OF YOUR GAMEMODE, OBJECTSTREAMER WONT WORK WITHOUT IT! 22 | AltEntitySync.Init( 1, 100, 23 | repository => new ServerEventNetworkLayer( repository ), 24 | ( ) => new LimitedGrid3( 50_000, 50_000, 100, 10_000, 10_000, 600 ), 25 | new IdProvider( ) 26 | ); 27 | ////////////////////////// 28 | 29 | AltAsync.OnPlayerConnect += OnPlayerConnect; 30 | AltAsync.OnConsoleCommand += OnConsoleCommand; 31 | 32 | // Spawn objects 33 | CreateObjects( ); 34 | 35 | // Display commands in console 36 | Console.ForegroundColor = ConsoleColor.Green; 37 | Console.WriteLine( "|---------------------AVAILABLE CONSOLE COMMANDS:---------------------|" ); 38 | Console.WriteLine( "| dao -> Destroy all created objects." ); 39 | Console.WriteLine( "| cao -> Create all objects defined in the CreateObjects method." ); 40 | Console.WriteLine( " " ); 41 | Console.WriteLine( "| cp {id} -> Move a specified object by 5 units on the Z axis(height)." ); 42 | Console.WriteLine( "| cr {id} -> Rotate a specified object by 5 units on the Z axis(yaw)." ); 43 | Console.WriteLine( "| cm {id} -> Change model of the specified object." ); 44 | Console.WriteLine( "| cld {id} -> Change LOD Distance of the specified object." ); 45 | Console.WriteLine( "| ctv {id} -> Change texture variation of the specified object." ); 46 | Console.WriteLine( "| cd {id} -> Change dynamic of the specified object." ); 47 | Console.WriteLine( "| cv {id} -> Change visibility of the specified object." ); 48 | Console.WriteLine( "| cof {id} -> Change on fire of the specified object." ); 49 | Console.WriteLine( "| cf {id} -> Change frozen of the specified object." ); 50 | Console.WriteLine( "| clc {id} -> Change light color of the specified object." ); 51 | Console.WriteLine( " " ); 52 | Console.WriteLine( "| do {id} -> Destroy a dynamic object by ID(IDs start at 0)." ); 53 | Console.WriteLine( "| go {id} -> Get dynamic object data of the specified object ID." ); 54 | Console.WriteLine( "| gc -> Get the dynamic object closest to player 1." ); 55 | Console.WriteLine( " " ); 56 | Console.WriteLine( "| countobj -> Get the amount of created objects." ); 57 | Console.WriteLine( "|--------------------------------------------------------------------|" ); 58 | Console.ResetColor( ); 59 | } 60 | 61 | private void CreateObjects( ) 62 | { 63 | // Create some objects 64 | ObjectStreamer.CreateDynamicObject( "port_xr_lifeboat", new Vector3( -859.655f, -803.499f, 25.566f ), new Vector3( 0, 0, 0 ), 0 ); 65 | ObjectStreamer.CreateDynamicObject( "bkr_prop_biker_bowlpin_stand", new Vector3( -959.655f, -903.499f, 25.566f ), new Vector3( 0, 0, 0 ), 0 ); 66 | ObjectStreamer.CreateDynamicObject( "bkr_prop_biker_tube_crn", new Vector3( -909.655f, -953.499f, 25.566f ), new Vector3( 0, 0, 0 ), 0 ); 67 | } 68 | 69 | private async Task OnConsoleCommand( string name, string[ ] args ) 70 | { 71 | // destroy all objects 72 | if( name == "dao" ) 73 | { 74 | ObjectStreamer.DestroyAllDynamicObjects( ); 75 | Console.WriteLine( $"all objects destroyed." ); 76 | } 77 | 78 | // create all objects 79 | if( name == "cao" ) 80 | { 81 | ObjectStreamer.DestroyAllDynamicObjects( ); 82 | CreateObjects( ); 83 | } 84 | 85 | // destroy object 86 | if( name == "do" ) 87 | { 88 | if( args.Length == 0 ) 89 | return; 90 | 91 | ulong objId = Convert.ToUInt64( args[ 0 ] ); 92 | if( ObjectStreamer.DestroyDynamicObject( objId ) ) 93 | { 94 | Console.WriteLine( $"Object with ID { objId } deleted!" ); 95 | } 96 | } 97 | 98 | // change rotation 99 | if( name == "cr" ) 100 | { 101 | if( args.Length == 0 ) 102 | return; 103 | 104 | ulong objId = Convert.ToUInt64( args[ 0 ] ); 105 | var obj = ObjectStreamer.GetDynamicObject( objId ); 106 | if( obj != null ) 107 | { 108 | Vector3 rot = obj.Rotation; 109 | obj.Rotation = new Vector3( rot.X, rot.Y, rot.Z + 5f ); 110 | Console.WriteLine( $"Object rotation increased on Z with +5f" ); 111 | } 112 | else 113 | Console.WriteLine( $"Couldnt find object with ID { objId }" ); 114 | } 115 | 116 | // change visible 117 | if( name == "cv" ) 118 | { 119 | if( args.Length == 0 ) 120 | return; 121 | 122 | ulong objId = Convert.ToUInt64( args[ 0 ] ); 123 | var obj = ObjectStreamer.GetDynamicObject( objId ); 124 | if( obj != null ) 125 | { 126 | obj.Visible = !obj.Visible; 127 | Console.WriteLine( $"Object visibility set to { obj.Visible }" ); 128 | } 129 | else 130 | Console.WriteLine( $"Couldnt find object with ID { objId }" ); 131 | } 132 | 133 | // change lod distance 134 | if( name == "cld" ) 135 | { 136 | if( args.Length == 0 ) 137 | return; 138 | 139 | ulong objId = Convert.ToUInt64( args[ 0 ] ); 140 | var obj = ObjectStreamer.GetDynamicObject( objId ); 141 | if( obj != null ) 142 | { 143 | obj.LodDistance += 100; 144 | Console.WriteLine( $"Object LOD Dist increased by 100" ); 145 | } 146 | else 147 | Console.WriteLine( $"Couldnt find object with ID { objId }" ); 148 | } 149 | 150 | // change texture variation 151 | if( name == "ctv" ) 152 | { 153 | if( args.Length == 0 ) 154 | return; 155 | 156 | ulong objId = Convert.ToUInt64( args[ 0 ] ); 157 | var obj = ObjectStreamer.GetDynamicObject( objId ); 158 | if( obj != null ) 159 | { 160 | var variations = Enum.GetValues( typeof( TextureVariation ) ); 161 | 162 | obj.TextureVariation = ( TextureVariation ) variations.GetValue( new Random( ).Next( variations.Length ) ); 163 | Console.WriteLine( $"Object texture variation changed to a random variation" ); 164 | } 165 | else 166 | Console.WriteLine( $"Couldnt find object with ID { objId }" ); 167 | } 168 | 169 | // change dynamic 170 | if( name == "cd" ) 171 | { 172 | if( args.Length == 0 ) 173 | return; 174 | 175 | ulong objId = Convert.ToUInt64( args[ 0 ] ); 176 | var obj = ObjectStreamer.GetDynamicObject( objId ); 177 | if( obj != null ) 178 | { 179 | obj.Dynamic = !obj.Dynamic; 180 | Console.WriteLine( $"Object dynamic changed to: { obj.Dynamic }" ); 181 | } 182 | else 183 | Console.WriteLine( $"Couldnt find object with ID { objId }" ); 184 | } 185 | 186 | // change on fire(EXPERIMENTAL, DOESNT WORK VERY WELL AS OF RIGHT NOW!) 187 | if( name == "cof" ) 188 | { 189 | if( args.Length == 0 ) 190 | return; 191 | 192 | ulong objId = Convert.ToUInt64( args[ 0 ] ); 193 | var obj = ObjectStreamer.GetDynamicObject( objId ); 194 | if( obj != null ) 195 | { 196 | obj.OnFire = !obj.OnFire; 197 | Console.WriteLine( $"Object on fire changed to: { obj.OnFire }" ); 198 | } 199 | else 200 | Console.WriteLine( $"Couldnt find object with ID { objId }" ); 201 | } 202 | 203 | // change frozen 204 | if( name == "cf" ) 205 | { 206 | if( args.Length == 0 ) 207 | return; 208 | 209 | ulong objId = Convert.ToUInt64( args[ 0 ] ); 210 | var obj = ObjectStreamer.GetDynamicObject( objId ); 211 | if( obj != null ) 212 | { 213 | obj.Frozen = !obj.Frozen; 214 | Console.WriteLine( $"Object frozen changed to: { obj.Frozen }" ); 215 | } 216 | else 217 | Console.WriteLine( $"Couldnt find object with ID { objId }" ); 218 | } 219 | 220 | // change light color 221 | if( name == "clc" ) 222 | { 223 | if( args.Length == 0 ) 224 | return; 225 | 226 | ulong objId = Convert.ToUInt64( args[ 0 ] ); 227 | var obj = ObjectStreamer.GetDynamicObject( objId ); 228 | if( obj != null ) 229 | { 230 | Random r = new Random( ); 231 | obj.LightColor = new Rgb( r.Next( 0, 256 ), r.Next( 0, 256 ), r.Next( 0, 256 ) ); 232 | Console.WriteLine( $"Object lightcolor changed to random value" ); 233 | } 234 | else 235 | Console.WriteLine( $"Couldnt find object with ID { objId }" ); 236 | } 237 | 238 | // change model 239 | if( name == "cm" ) 240 | { 241 | if( args.Length == 0 ) 242 | return; 243 | 244 | ulong objId = Convert.ToUInt64( args[ 0 ] ); 245 | var obj = ObjectStreamer.GetDynamicObject( objId ); 246 | if( obj != null ) 247 | { 248 | // change object into a house 249 | obj.Model = "lf_house_17_"; 250 | Console.WriteLine( $"Object changed into a house." ); 251 | } 252 | else 253 | Console.WriteLine( $"Couldnt find object with ID { objId }" ); 254 | } 255 | 256 | // change pos 257 | if( name == "cp" ) 258 | { 259 | if( args.Length == 0 ) 260 | return; 261 | 262 | ulong objId = Convert.ToUInt64( args[ 0 ] ); 263 | var obj = ObjectStreamer.GetDynamicObject( objId ); 264 | if( obj != null ) 265 | { 266 | Console.WriteLine( $"obj pos: { obj.Position.Z }" ); 267 | 268 | obj.Position += new Vector3( 0, 0, 5 ); 269 | Console.WriteLine( $"Object position increased on Z with +5f { obj.Position.Z }" ); 270 | } 271 | else 272 | Console.WriteLine( $"Couldnt find object with ID { objId }" ); 273 | } 274 | 275 | // get object by ID 276 | if( name == "go" ) 277 | { 278 | if( args.Length == 0 ) 279 | return; 280 | 281 | ulong objId = Convert.ToUInt64( args[ 0 ] ); 282 | var obj = ObjectStreamer.GetDynamicObject( objId ); 283 | if( obj != null ) 284 | { 285 | Console.WriteLine( $"Object found, data: { obj.Model }, { obj.Rotation.X }, { obj.Rotation.Y }, { obj.Rotation.Z }, { obj.Frozen }, ...!" ); 286 | } 287 | else 288 | Console.WriteLine( $"Couldnt find object with ID { objId }" ); 289 | } 290 | 291 | // get closest object 292 | if( name == "gc" ) 293 | { 294 | IPlayer player = Alt.GetAllPlayers( ).First( ); 295 | 296 | if( player != null ) 297 | { 298 | (DynamicObject obj, float distance) = ObjectStreamer.GetClosestDynamicObject( player.Position ); 299 | 300 | if( obj == null ) 301 | { 302 | Console.WriteLine( "Couldn't find any object near player." ); 303 | return; 304 | } 305 | 306 | Console.WriteLine( $"Closest object ID is { obj.Id } at a distance of { distance }." ); 307 | } 308 | else 309 | Console.WriteLine( $"Couldnt find any players." ); 310 | } 311 | 312 | // count objects 313 | if( name == "countobj" ) 314 | { 315 | Console.WriteLine( $"total objects created: { ObjectStreamer.GetAllDynamicObjects( ).Count }" ); 316 | } 317 | } 318 | 319 | private async Task OnPlayerConnect( IPlayer player, string reason ) 320 | { 321 | Console.WriteLine( $"{ player.Name } connected!" ); 322 | player.Model = ( uint ) AltV.Net.Enums.PedModel.FreemodeMale01; 323 | player.Spawn( new Position( -889.655f, -853.499f, 20.566f ), 0 ); 324 | } 325 | 326 | public override void OnStop( ) 327 | { 328 | ObjectStreamer.DestroyAllDynamicObjects( ); 329 | Console.WriteLine( $"Server stopped." ); 330 | } 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /server-scripts/ObjectStreamer.cs: -------------------------------------------------------------------------------- 1 | using AltV.Net; 2 | using AltV.Net.EntitySync; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Numerics; 6 | 7 | namespace DasNiels.AltV.Streamers 8 | { 9 | public enum TextureVariation 10 | { 11 | Pacific = 0, 12 | Azure = 1, 13 | Nautical = 2, 14 | Continental = 3, 15 | Battleship = 4, 16 | Intrepid = 5, 17 | Uniform = 6, 18 | Classico = 7, 19 | Mediterranean = 8, 20 | Command = 9, 21 | Mariner = 10, 22 | Ruby = 11, 23 | Vintage = 12, 24 | Pristine = 13, 25 | Merchant = 14, 26 | Voyager = 15 27 | } 28 | 29 | public class MoveData : IWritable 30 | { 31 | public float X { get; set; } 32 | public float Y { get; set; } 33 | public float Z { get; set; } 34 | public float Speed { get; set; } 35 | public void OnWrite( IMValueWriter writer ) 36 | { 37 | writer.BeginObject( ); 38 | writer.Name( "X" ); 39 | writer.Value( X ); 40 | writer.Name( "Y" ); 41 | writer.Value( "Y" ); 42 | writer.Name( "Z" ); 43 | writer.Value( Z ); 44 | writer.Name( "Speed" ); 45 | writer.Value( Speed ); 46 | writer.EndObject( ); 47 | } 48 | } 49 | 50 | public class Rgb : IWritable 51 | { 52 | public int Red { get; set; } 53 | public int Green { get; set; } 54 | public int Blue { get; set; } 55 | 56 | public Rgb( int red, int green, int blue ) 57 | { 58 | Red = red; 59 | Green = green; 60 | Blue = blue; 61 | } 62 | 63 | public void OnWrite( IMValueWriter writer ) 64 | { 65 | writer.BeginObject( ); 66 | writer.Name( "Red" ); 67 | writer.Value( Red ); 68 | writer.Name( "Green" ); 69 | writer.Value( Green ); 70 | writer.Name( "Blue" ); 71 | writer.Value( Blue ); 72 | writer.EndObject( ); 73 | } 74 | } 75 | 76 | /// 77 | /// DynamicObject class that stores all data related to a single object 78 | /// 79 | public class DynamicObject : Entity, IEntity 80 | { 81 | public string EntityType 82 | { 83 | get 84 | { 85 | if( !TryGetData( "entityType", out string type ) ) 86 | return null; 87 | 88 | return type; 89 | } 90 | set 91 | { 92 | // No data changed 93 | if( EntityType == value ) 94 | return; 95 | 96 | SetData( "entityType", value ); 97 | } 98 | } 99 | 100 | /// 101 | /// Set or get the current object's rotation (in degrees). 102 | /// 103 | public Vector3 Rotation 104 | { 105 | get 106 | { 107 | if( !TryGetData( "rotation", out Dictionary data ) ) 108 | return default; 109 | 110 | return new Vector3( ) 111 | { 112 | X = Convert.ToSingle( data[ "x" ] ), 113 | Y = Convert.ToSingle( data[ "y" ] ), 114 | Z = Convert.ToSingle( data[ "z" ] ), 115 | }; 116 | } 117 | set 118 | { 119 | // No data changed 120 | if( Rotation != null && Rotation.X == value.X && Rotation.Y == value.Y && Rotation.Z == value.Z && value != new Vector3( 0, 0, 0 ) ) 121 | return; 122 | 123 | Dictionary dict = new Dictionary( ) 124 | { 125 | [ "x" ] = value.X, 126 | [ "y" ] = value.Y, 127 | [ "z" ] = value.Z, 128 | }; 129 | SetData( "rotation", dict ); 130 | } 131 | } 132 | 133 | /// 134 | /// Set or get the current object's model. 135 | /// 136 | public string Model 137 | { 138 | get 139 | { 140 | if( !TryGetData( "model", out string model ) ) 141 | return null; 142 | 143 | return model; 144 | } 145 | set 146 | { 147 | // No data changed 148 | if( Model == value ) 149 | return; 150 | 151 | SetData( "model", value ); 152 | } 153 | } 154 | 155 | /// 156 | /// Set or get LOD Distance of the object. 157 | /// 158 | public uint? LodDistance 159 | { 160 | get 161 | { 162 | if( !TryGetData( "lodDistance", out uint lodDist ) ) 163 | return null; 164 | 165 | return lodDist; 166 | } 167 | set 168 | { 169 | // if value is set to null, reset the data 170 | if( value == null ) 171 | { 172 | SetData( "lodDistance", null ); 173 | return; 174 | } 175 | 176 | // No data changed 177 | if( LodDistance == value ) 178 | return; 179 | 180 | SetData( "lodDistance", value ); 181 | } 182 | } 183 | 184 | /// 185 | /// Get or set the current texture variation, use null to reset it to default. 186 | /// 187 | public TextureVariation? TextureVariation 188 | { 189 | get 190 | { 191 | if( !TryGetData( "textureVariation", out int variation ) ) 192 | return null; 193 | 194 | return ( TextureVariation ) variation; 195 | } 196 | set 197 | { 198 | // if value is set to null, reset the data 199 | if( value == null ) 200 | { 201 | SetData( "textureVariation", null ); 202 | return; 203 | } 204 | 205 | // No data changed 206 | if( TextureVariation == value ) 207 | return; 208 | 209 | SetData( "textureVariation", ( int ) value ); 210 | } 211 | } 212 | 213 | /// 214 | /// Get or set the object's dynamic state. Some objects can be moved around by the player when dynamic is set to true. 215 | /// 216 | public bool? Dynamic 217 | { 218 | get 219 | { 220 | if( !TryGetData( "dynamic", out bool isDynamic ) ) 221 | return false; 222 | 223 | return isDynamic; 224 | } 225 | set 226 | { 227 | // if value is set to null, reset the data 228 | if( value == null ) 229 | { 230 | SetData( "dynamic", null ); 231 | return; 232 | } 233 | 234 | // No data changed 235 | if( Dynamic == value ) 236 | return; 237 | 238 | SetData( "dynamic", value ); 239 | } 240 | } 241 | 242 | /// 243 | /// Set/get visibility state of object 244 | /// 245 | public bool? Visible 246 | { 247 | get 248 | { 249 | if( !TryGetData( "visible", out bool visible ) ) 250 | return false; 251 | 252 | return visible; 253 | } 254 | set 255 | { 256 | // if value is set to null, reset the data 257 | if( value == null ) 258 | { 259 | SetData( "visible", null ); 260 | return; 261 | } 262 | 263 | // No data changed 264 | if( Visible == value ) 265 | return; 266 | 267 | SetData( "visible", value ); 268 | } 269 | } 270 | 271 | /// 272 | /// Set/get an object on fire, NOTE: does not work very well as of right now, fire is very small. 273 | /// 274 | public bool? OnFire 275 | { 276 | get 277 | { 278 | if( !TryGetData( "onFire", out bool onFire ) ) 279 | return false; 280 | 281 | return onFire; 282 | } 283 | set 284 | { 285 | // if value is set to null, reset the data 286 | if( value == null ) 287 | { 288 | SetData( "onFire", null ); 289 | return; 290 | } 291 | 292 | // No data changed 293 | if( OnFire == value ) 294 | return; 295 | 296 | SetData( "onFire", value ); 297 | } 298 | } 299 | 300 | /// 301 | /// Freeze an object into it's current position. or get it's status 302 | /// 303 | public bool? Frozen 304 | { 305 | get 306 | { 307 | if( !TryGetData( "frozen", out bool frozen ) ) 308 | return false; 309 | 310 | return frozen; 311 | } 312 | set 313 | { 314 | // if value is set to null, reset the data 315 | if( value == null ) 316 | { 317 | SetData( "frozen", null ); 318 | return; 319 | } 320 | 321 | // No data changed 322 | if( Frozen == value ) 323 | return; 324 | 325 | SetData( "frozen", value ); 326 | } 327 | } 328 | 329 | /// 330 | /// Set the light color of the object, use null to reset it to default. 331 | /// 332 | public Rgb LightColor 333 | { 334 | get 335 | { 336 | if( !TryGetData( "lightColor", out Dictionary data ) ) 337 | return null; 338 | 339 | return new Rgb( 340 | Convert.ToInt32( data[ "r" ] ), 341 | Convert.ToInt32( data[ "g" ] ), 342 | Convert.ToInt32( data[ "b" ] ) 343 | ); 344 | } 345 | set 346 | { 347 | // if value is set to null, reset the data 348 | if( value == null ) 349 | { 350 | SetData( "lightColor", null ); 351 | return; 352 | } 353 | 354 | // No data changed 355 | if( LightColor != null && LightColor.Red == value.Red && LightColor.Green == value.Green && LightColor.Blue == value.Blue ) 356 | return; 357 | 358 | Dictionary dict = new Dictionary 359 | { 360 | { "r", value.Red }, 361 | { "g", value.Green }, 362 | { "b", value.Blue } 363 | }; 364 | SetData( "lightColor", dict ); 365 | } 366 | } 367 | 368 | public DynamicObject( Vector3 position, int dimension, uint range, string entityType ) : base( 0, position, dimension, range ) 369 | { 370 | EntityType = entityType; 371 | } 372 | 373 | public void Destroy( ) 374 | { 375 | AltEntitySync.RemoveEntity( this ); 376 | } 377 | } 378 | 379 | 380 | public static class ObjectStreamer 381 | { 382 | /// 383 | /// Create a new dynamic object. 384 | /// 385 | /// The object model name. 386 | /// The position to spawn the object at. 387 | /// The rotation to spawn the object at(degrees). 388 | /// The dimension to spawn the object in. 389 | /// (Optional): Set object dynamic or not. 390 | /// (Optional): Set object frozen. 391 | /// (Optional): Set LOD distance. 392 | /// (Optional): set light color. 393 | /// (Optional): set object on fire(DOESN'T WORK PROPERLY YET!) 394 | /// (Optional): Set object texture variation. 395 | /// (Optional): Set object visibility. 396 | /// (Optional): The range that a player has to be in before the object spawns, default value is 400. 397 | /// The newly created dynamic object. 398 | public static DynamicObject CreateDynamicObject( 399 | string model, Vector3 position, Vector3 rotation, int dimension = 0, bool? isDynamic = null, bool? frozen = null, uint? lodDistance = null, 400 | Rgb lightColor = null, bool? onFire = null, TextureVariation? textureVariation = null, bool? visible = null, uint streamRange = 400 401 | ) 402 | { 403 | DynamicObject obj = new DynamicObject( position, dimension, streamRange, "object" ) 404 | { 405 | Rotation = rotation, 406 | Model = model, 407 | Dynamic = isDynamic ?? null, 408 | Frozen = frozen ?? null, 409 | LodDistance = lodDistance ?? null, 410 | LightColor = lightColor ?? null, 411 | OnFire = onFire ?? null, 412 | TextureVariation = textureVariation ?? null, 413 | Visible = visible ?? null 414 | }; 415 | 416 | AltEntitySync.AddEntity( obj ); 417 | return obj; 418 | } 419 | 420 | /// 421 | /// Destroy a dynamic object by it's ID. 422 | /// 423 | /// The ID of the object. 424 | /// True if successful, false otherwise. 425 | public static bool DestroyDynamicObject( ulong dynamicObjectId ) 426 | { 427 | DynamicObject obj = GetDynamicObject( dynamicObjectId ); 428 | 429 | if( obj == null ) 430 | return false; 431 | 432 | AltEntitySync.RemoveEntity( obj ); 433 | return true; 434 | } 435 | 436 | /// 437 | /// Destroy a dynamic object. 438 | /// 439 | /// The object instance to destroy 440 | /// 441 | public static void DestroyDynamicObject( DynamicObject obj ) 442 | { 443 | AltEntitySync.RemoveEntity( obj ); 444 | } 445 | 446 | /// 447 | /// Get a dynamic object by it's ID. 448 | /// 449 | /// The ID of the object. 450 | /// The dynamic object or null if not found. 451 | public static DynamicObject GetDynamicObject( ulong dynamicObjectId ) 452 | { 453 | if( !AltEntitySync.TryGetEntity( dynamicObjectId, out IEntity entity ) ) 454 | { 455 | Console.WriteLine( $"[OBJECT-STREAMER] [GetDynamicObject] ERROR: Entity with ID { dynamicObjectId } couldn't be found." ); 456 | return default; 457 | } 458 | 459 | if( !( entity is DynamicObject ) ) 460 | return default; 461 | 462 | return ( DynamicObject ) entity; 463 | } 464 | 465 | /// 466 | /// Destroy all created dynamic objects. 467 | /// 468 | public static void DestroyAllDynamicObjects( ) 469 | { 470 | foreach( DynamicObject obj in GetAllDynamicObjects( ) ) 471 | { 472 | AltEntitySync.RemoveEntity( obj ); 473 | } 474 | } 475 | 476 | /// 477 | /// Get all created dynamic objects. 478 | /// 479 | /// A list of dynamic objects. 480 | public static List GetAllDynamicObjects( ) 481 | { 482 | List objects = new List( ); 483 | 484 | foreach( IEntity entity in AltEntitySync.GetAllEntities( ) ) 485 | { 486 | DynamicObject obj = GetDynamicObject( entity.Id ); 487 | 488 | if( obj != null ) 489 | objects.Add( obj ); 490 | } 491 | 492 | return objects; 493 | } 494 | 495 | /// 496 | /// Get the dynamic object that's closest to a specified position. 497 | /// 498 | /// The position from which to check. 499 | /// The closest dynamic object to the specified position, or null if none found. 500 | public static (DynamicObject obj, float distance) GetClosestDynamicObject( Vector3 pos ) 501 | { 502 | if( GetAllDynamicObjects( ).Count == 0 ) 503 | return (null, 5000); 504 | 505 | DynamicObject obj = null; 506 | float distance = 5000; 507 | 508 | foreach( DynamicObject o in GetAllDynamicObjects( ) ) 509 | { 510 | float dist = Vector3.Distance( o.Position, pos ); 511 | if( dist < distance ) 512 | { 513 | obj = o; 514 | distance = dist; 515 | } 516 | } 517 | 518 | return (obj, distance); 519 | } 520 | } 521 | } 522 | --------------------------------------------------------------------------------