├── ClientSide
└── resources
│ ├── sarp-streamer-door
│ ├── resource.cfg
│ ├── client.mjs
│ └── door-streamer.mjs
│ ├── sarp-streamer-text
│ ├── resource.cfg
│ ├── client.mjs
│ └── textlabel-streamer.mjs
│ ├── sarp-streamer-helptext
│ ├── resource.cfg
│ ├── helptext-streamer.mjs
│ └── client.mjs
│ ├── sarp-streamer-marker
│ ├── resource.cfg
│ ├── client.mjs
│ └── marker-streamer.mjs
│ ├── sarp-streamer-blip
│ ├── dynamic-blip-streamer.mjs
│ ├── static-blip-streamer.mjs
│ ├── resource.cfg
│ └── client.mjs
│ └── sarp-streamer-object
│ ├── resource.cfg
│ ├── async-models.mjs
│ ├── client.mjs
│ └── object-streamer.mjs
├── .github
└── ISSUE_TEMPLATE
│ └── bug_report.md
├── LICENSE
├── ServerSide
├── Override
│ └── CustomSpatialPartition.cs
├── DoorManager.cs
├── HelpTextManager.cs
├── BlipManager.cs
├── PlayerLabelManager.cs
├── PropManager.cs
└── MarkerManager.cs
└── README.md
/ClientSide/resources/sarp-streamer-door/resource.cfg:
--------------------------------------------------------------------------------
1 | type: js
2 | client-main: client.mjs
3 |
4 | client-files: [
5 | client.mjs,
6 | door-streamer.mjs
7 | ]
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-text/resource.cfg:
--------------------------------------------------------------------------------
1 | type: js
2 | client-main: client.mjs
3 |
4 | client-files: [
5 | client.mjs,
6 | textlabel-streamer.mjs
7 | ]
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-helptext/resource.cfg:
--------------------------------------------------------------------------------
1 | type: js
2 | client-main: client.mjs
3 |
4 | client-files: [
5 | client.mjs,
6 | helptext-streamer.mjs
7 | ]
8 |
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-marker/resource.cfg:
--------------------------------------------------------------------------------
1 | type: js
2 | client-main: client.mjs
3 |
4 | client-files: [
5 | client.mjs,
6 | marker-streamer.mjs,
7 | ]
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-blip/dynamic-blip-streamer.mjs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LameuleFR/altv-csharp-streamer/HEAD/ClientSide/resources/sarp-streamer-blip/dynamic-blip-streamer.mjs
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-blip/static-blip-streamer.mjs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LameuleFR/altv-csharp-streamer/HEAD/ClientSide/resources/sarp-streamer-blip/static-blip-streamer.mjs
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-helptext/helptext-streamer.mjs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LameuleFR/altv-csharp-streamer/HEAD/ClientSide/resources/sarp-streamer-helptext/helptext-streamer.mjs
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-object/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 | ]
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-blip/resource.cfg:
--------------------------------------------------------------------------------
1 | type: js
2 | client-main: client.mjs
3 |
4 | client-files: [
5 | client.mjs,
6 | dynamic-blip-streamer.mjs
7 | static-blip-streamer.mjs
8 | ]
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 |
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Additional context**
24 | Add any other context about the problem here.
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 lameule123
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 |
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-helptext/client.mjs:
--------------------------------------------------------------------------------
1 | import * as alt from 'alt';
2 | import { helpTextStreamer } from "./helptext-streamer";
3 |
4 | // when an object is streamed in
5 | alt.onServer("entitySync:create", (entityId, entityType, position, currEntityData) => {
6 | if (currEntityData) {
7 | let data = currEntityData;
8 | if (data != undefined) {
9 | if (entityType === 3) {
10 | helpTextStreamer.addHelpText(
11 | +entityId, data.text, position, +entityType
12 | );
13 | }
14 | }
15 | } else {
16 | if (entityType === 3) {
17 | helpTextStreamer.restoreHelpText(+entityId);
18 | }
19 | }
20 | });
21 |
22 | // when an object is streamed out
23 | alt.onServer("entitySync:remove", (entityId, entityType) => {
24 | if (entityType === 3) {
25 | helpTextStreamer.removeHelpText(+entityId);
26 | }
27 | });
28 |
29 | // when a streamed in object changes position data
30 | alt.onServer("entitySync:updatePosition", (entityId, entityType, position) => {
31 | if (entityType === 3) {
32 | helpTextStreamer.setPosition(+entityId, position);
33 | }
34 | });
35 |
36 | // when a streamed in object changes data
37 | alt.onServer("entitySync:updateData", (entityId, entityType, newEntityData) => {
38 | if (entityType === 3) {
39 | if (newEntityData.hasOwnProperty("text"))
40 | helpTextStreamer.setText(+entityId, newEntityData.text);
41 | }
42 | });
43 |
44 | // when a streamed in object needs to be removed
45 | alt.onServer("entitySync:clearCache", (entityId, entityType) => {
46 | if (entityType === 3) {
47 | helpTextStreamer.clearHelpText(+entityId);
48 | }
49 | });
50 |
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-door/client.mjs:
--------------------------------------------------------------------------------
1 | import * as alt from 'alt-client';
2 | import { doorStreamer } from "./door-streamer";
3 | const ThreadID = 6;
4 |
5 | // when an object is streamed in
6 | alt.onServer("entitySync:create", (entityId, entityType, position, currEntityData) => {
7 | if (currEntityData) {
8 | let data = currEntityData;
9 | if (data != undefined) {
10 | if (entityType === ThreadID) {
11 | doorStreamer.addDoor(
12 | +entityId, position, data.heading, +entityType, data.hash, data.locked
13 | );
14 | }
15 | }
16 | } else {
17 | if (entityType === ThreadID) {
18 | doorStreamer.restoreDoor(+entityId);
19 | }
20 | }
21 | });
22 |
23 | // when an object is streamed out
24 | alt.onServer("entitySync:remove", (entityId, entityType) => {
25 | if (entityType === ThreadID) {
26 | doorStreamer.removeDoor(+entityId);
27 | }
28 | });
29 |
30 | // when a streamed in object changes position data
31 | alt.onServer("entitySync:updatePosition", (entityId, entityType, position) => {
32 | if (entityType === ThreadID) {
33 | doorStreamer.setPosition(+entityId, position);
34 | }
35 | });
36 |
37 | // when a streamed in object changes data
38 | alt.onServer("entitySync:updateData", (entityId, entityType, newEntityData) => {
39 | if (entityType === ThreadID) {
40 | if (newEntityData.hasOwnProperty("locked"))
41 | doorStreamer.setState(+entityId, newEntityData.locked);
42 | }
43 | });
44 |
45 | // when a streamed in object needs to be removed
46 | alt.onServer("entitySync:clearCache", (entityId, entityType) => {
47 | if (entityType === ThreadID) {
48 | doorStreamer.clearDoor(+entityId);
49 | }
50 | });
51 |
--------------------------------------------------------------------------------
/ServerSide/Override/CustomSpatialPartition.cs:
--------------------------------------------------------------------------------
1 | using AltV.Net.EntitySync;
2 | using AltV.Net.EntitySync.SpatialPartitions;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Numerics;
6 |
7 | ///
8 | /// THIS OVERRIDE IS ONLY USED BY BLIPMANAGER(Static) CAUSE THIS TYPE OF BLIP ARE ONLY DESIGNED TO BE STATIC.
9 | /// WE NEED TO VERIFY DIMENSION ONLY INSTEAD OF POSITION + DIMENSION.
10 | ///
11 | namespace EntityStreamer
12 | {
13 | public class GlobalEntity : SpatialPartition
14 | {
15 | private readonly HashSet entities = new HashSet();
16 |
17 | public GlobalEntity()
18 | {
19 | }
20 |
21 | public override void Add(IEntity entity)
22 | {
23 | entities.Add(entity);
24 | }
25 |
26 | public override void Remove(IEntity entity)
27 | {
28 | entities.Remove(entity);
29 | }
30 |
31 | public override void UpdateEntityPosition(IEntity entity, in Vector3 newPosition)
32 | {
33 | }
34 |
35 | public override void UpdateEntityRange(IEntity entity, uint range)
36 | {
37 | }
38 |
39 | public override void UpdateEntityDimension(IEntity entity, int dimension)
40 | {
41 | }
42 |
43 | private static bool CanSeeOtherDimension(int dimension, int otherDimension)
44 | {
45 | if (dimension > 0) return dimension == otherDimension || otherDimension == int.MinValue;
46 | if (dimension < 0)
47 | return otherDimension == 0 || dimension == otherDimension || otherDimension == int.MinValue;
48 | return otherDimension == 0 || otherDimension == int.MinValue;
49 | }
50 |
51 | public override IList Find(Vector3 position, int dimension)
52 | {
53 | return entities.Where(entity => CanSeeOtherDimension(dimension, entity.Dimension)).ToList();
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-object/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, entityType ) {
14 | this.loadingModels.delete( +entityId, +entityType );
15 | }
16 |
17 | async load( entityId, entityType, model ) {
18 | return new Promise( resolve => {
19 | if( !natives.isModelValid( model ) )
20 | return done( false );
21 |
22 | if( natives.hasModelLoaded( model ) )
23 | return done( true );
24 |
25 | if( typeof model === 'string' )
26 | model = alt.hash( model );
27 |
28 | this.loadingModels.add( +entityId, +entityType );
29 |
30 | natives.requestModel( model );
31 |
32 | const interval = alt.setInterval( () => {
33 | if( !this.loadingModels.has( +entityId, +entityType ) ) {
34 | return done( !!natives.hasModelLoaded( model ) );
35 | }
36 |
37 | if( natives.hasModelLoaded( model ) ) {
38 | return done( true );
39 | }
40 | }, 2 );
41 |
42 | const timeout = alt.setTimeout( ( ) => {
43 | return done( !!natives.hasModelLoaded( model ) );
44 | }, 3000 );
45 |
46 | const done = result => {
47 | alt.clearInterval( interval );
48 | alt.clearTimeout( timeout );
49 |
50 | this.loadingModels.delete( +entityId, +entityType );
51 | resolve( result );
52 | };
53 | } );
54 | }
55 | }
56 |
57 | export const asyncModel = new AsyncModel();
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # altv-csharp-streamer
2 | ServerSide Streamer using EntitySync.
3 | Initially Based on @DasNiels Work.
4 |
5 | # Current Streamer Available
6 | - TextLabel
7 | - HelpText
8 | - Marker
9 | - Object
10 | - Blip (Static/Dynamic)
11 | - Door
12 |
13 | # Installation
14 | This resource makes use of the AltV.Net.EntitySync (v1.13.0) and AltV.Net.EntitySync.ServerEvent (v9.0.2) nuget package, make sure to install those prior to using this resource.
15 |
16 | Make sure to add the following code to your gamemode's OnStart() method(the streamer won't work without it!):
17 | // Documentation: https://docs.altv.mp/cs/articles/entity-sync.html
18 |
19 | ```
20 | AltEntitySync.Init(8, (syncrate) => 200, (threadId) => false,
21 | (threadCount, repository) => new ServerEventNetworkLayer(threadCount, repository),
22 | (entity, threadCount) => entity.Type,
23 | (entityId, entityType, threadCount) => entityType,
24 | (threadId) =>
25 | {
26 | return threadId switch
27 | {
28 | //MARKER
29 | 0 => new LimitedGrid3(50_000, 50_000, 75, 10_000, 10_000, 64),
30 | //TEXT
31 | 1 => new LimitedGrid3(50_000, 50_000, 75, 10_000, 10_000, 32),
32 | //PROP
33 | 2 => new LimitedGrid3(50_000, 50_000, 100, 10_000, 10_000, 1500),
34 | //HELP TEXT
35 | 3 => new LimitedGrid3(50_000, 50_000, 100, 10_000, 10_000, 1),
36 | //BLIP
37 | 4 => new EntityStreamer.GlobalEntity(),
38 | //DYNAMIC BLIP
39 | 5 => new LimitedGrid3(50_000, 50_000, 175, 10_000, 10_000, 200),
40 | //DOOR
41 | 6 => new LimitedGrid3(50_000, 50_000, 175, 10_000, 10_000, 50),
42 | _ => new LimitedGrid3(50_000, 50_000, 175, 10_000, 10_000, 115),
43 | };
44 | },
45 | new IdProvider());
46 | ```
47 |
48 | # Credits
49 | Original Object Streamer: https://github.com/DasNiels/altv-object-streamer/
50 | Original Text Streamer: https://github.com/DasNiels/altv-textlabel-streamer
51 | Original Marker Streamer: https://github.com/DasNiels/altv-marker-streamer
52 |
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-door/door-streamer.mjs:
--------------------------------------------------------------------------------
1 | import * as alt from 'alt-client';
2 | import * as native from 'natives';
3 |
4 | class DoorStreamer {
5 | constructor() {
6 | this.doors = [];
7 | }
8 | async addDoor(entityId, position, heading, entityType, hash, locked) {
9 | this.clearDoor(+entityId);
10 | let door = {
11 | hash: hash, locked: locked, position: position,
12 | heading: heading, entityId: +entityId, entityType: +entityType,
13 | };
14 | this.doors[entityId] = door;
15 | this.setState(entityId, locked);
16 | }
17 | getDoor(entityId) {
18 | if (this.doors.hasOwnProperty(entityId)) {
19 | return this.doors[entityId];
20 | }
21 | else {
22 | return null;
23 | }
24 | }
25 | restoreDoor(entityId) {
26 | if (this.doors.hasOwnProperty(entityId)) {
27 | let door = this.doors[entityId];
28 | this.setState(entityId, door.locked);
29 | }
30 | }
31 | removeDoor(entityId) {
32 | if (this.doors.hasOwnProperty(entityId)) {
33 | delete this.doors[entityId];
34 | }
35 | }
36 | clearDoor(entityId) {
37 | if (this.doors.hasOwnProperty(entityId)) {
38 | delete this.doors[entityId];
39 | }
40 | }
41 | clearAllDoor() {
42 | this.doors = [];
43 | }
44 | setPosition(entityId, pos) {
45 | if (this.doors.hasOwnProperty(entityId)) {
46 | this.doors[entityId].position = pos;
47 | }
48 | }
49 | setState(entityId, locked) {
50 | if (this.doors.hasOwnProperty(entityId)) {
51 | let door = this.doors[entityId];
52 | native.setStateOfClosestDoorOfType(door.hash,
53 | door.position.x,
54 | door.position.y,
55 | door.position.z,
56 | locked,
57 | door.heading,
58 | false);
59 | native.doorControl(door.hash, door.position.x, door.position.y, door.position.z, door.locked, 0.0, door.heading, 0.0);
60 | }
61 | }
62 | }
63 | export const doorStreamer = new DoorStreamer();
64 |
65 | alt.on("resourceStop", () => {
66 | doorStreamer.clearAllDoor();
67 | });
68 |
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-text/client.mjs:
--------------------------------------------------------------------------------
1 | import * as alt from 'alt';
2 | import { textLabelStreamer } from "./textlabel-streamer";
3 |
4 | // when an object is streamed in
5 | alt.onServer("entitySync:create", (entityId, entityType, position, currEntityData) => {
6 | if( currEntityData ) {
7 | let data = currEntityData;
8 | if(data != undefined) {
9 | if (entityType == 1){
10 | textLabelStreamer.addTextLabel(
11 | +entityId, data.text, position, data.scale, data.font, data.color, data.dropShadow, data.edge, data.center, data.proportional, +entityType
12 | );
13 | }
14 | }
15 | }else{
16 | if (entityType == 1){
17 | textLabelStreamer.restoreTextLabel( +entityId );
18 | }
19 | }
20 | });
21 |
22 | // when an object is streamed out
23 | alt.onServer("entitySync:remove", (entityId, entityType) => {
24 | if (entityType == 1){
25 | textLabelStreamer.removeTextLabel( +entityId );
26 | }
27 | } );
28 |
29 | // when a streamed in object changes position data
30 | alt.onServer("entitySync:updatePosition", (entityId, entityType, position) => {
31 | if (entityType == 1){
32 | textLabelStreamer.setPosition( +entityId, position );
33 | }
34 | } );
35 |
36 | // when a streamed in object changes data
37 | alt.onServer("entitySync:updateData", (entityId, entityType, newEntityData) => {
38 | if (entityType == 1){
39 | if( newEntityData.hasOwnProperty( "center" ) )
40 | textLabelStreamer.setCenter( +entityId , newEntityData.center );
41 |
42 | if( newEntityData.hasOwnProperty( "color" ) )
43 | textLabelStreamer.setColor( +entityId , newEntityData.color );
44 |
45 | if( newEntityData.hasOwnProperty( "center" ) )
46 | textLabelStreamer.setDropShadow( +entityId , newEntityData.center );
47 |
48 | if( newEntityData.hasOwnProperty( "edge" ) )
49 | textLabelStreamer.setEdge( +entityId , newEntityData.edge );
50 |
51 | if( newEntityData.hasOwnProperty( "font" ) )
52 | textLabelStreamer.setFont( +entityId , newEntityData.font );
53 |
54 | if( newEntityData.hasOwnProperty( "proportional" ) )
55 | textLabelStreamer.setProportional( +entityId , newEntityData.proportional );
56 |
57 | if( newEntityData.hasOwnProperty( "scale" ) )
58 | textLabelStreamer.setScale( +entityId , newEntityData.scale );
59 |
60 | if( newEntityData.hasOwnProperty( "text" ) )
61 | textLabelStreamer.setText( +entityId , newEntityData.text );
62 | }
63 | } );
64 |
65 | // when a streamed in object needs to be removed
66 | alt.onServer("entitySync:clearCache", (entityId, entityType) => {
67 | if (entityType == 1){
68 | textLabelStreamer.clearTextLabel( +entityId );
69 | }
70 | } );
71 |
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-blip/client.mjs:
--------------------------------------------------------------------------------
1 | import * as alt from 'alt';
2 | import { staticBlipStreamer } from "./static-blip-streamer";
3 | import { dynamicBlipStreamer } from "./dynamic-blip-streamer";
4 | const BLIPTYPE = 4;
5 | const BLIPTYPE2 = 5;
6 |
7 | // when an object is streamed in
8 | alt.onServer("entitySync:create", (entityId, entityType, position, currEntityData) => {
9 | if (currEntityData) {
10 | let data = currEntityData;
11 | if (data != undefined) {
12 | if (entityType === BLIPTYPE) {
13 | staticBlipStreamer.addBlip(
14 | +entityId, position, data.name, data.sprite, data.color, data.scale, data.shortRange
15 | );
16 | }else if (entityType === BLIPTYPE2){
17 | dynamicBlipStreamer.addBlip(
18 | +entityId, position, data.name, data.sprite, data.color, data.scale, data.shortRange
19 | );
20 | }
21 | }
22 | } else {
23 | if (entityType === BLIPTYPE) {
24 | staticBlipStreamer.restoreBlip(+entityId);
25 | }else if (entityType === BLIPTYPE2){
26 | dynamicBlipStreamer.restoreBlip(+entityId);
27 | }
28 | }
29 | });
30 |
31 | // when an object is streamed out
32 | alt.onServer("entitySync:remove", (entityId, entityType) => {
33 | if (entityType == BLIPTYPE) {
34 | staticBlipStreamer.removeBlip(+entityId, false);
35 | }else if (entityType == BLIPTYPE2){
36 | dynamicBlipStreamer.removeBlip(+entityId, false);
37 | }
38 | });
39 |
40 | // when a streamed in object changes position data
41 | alt.onServer("entitySync:updatePosition", (entityId, entityType, position) => {
42 | if (entityType == BLIPTYPE) {
43 | staticBlipStreamer.setPosition(+entityId, position);
44 | }else if (entityType == BLIPTYPE2){
45 | dynamicBlipStreamer.setPosition(+entityId, position);
46 | }
47 | });
48 |
49 | // when a streamed in object changes data
50 | alt.onServer("entitySync:updateData", (entityId, entityType, newEntityData) => {
51 | if (entityType == BLIPTYPE) {
52 | if (newEntityData.hasOwnProperty("text"))
53 | staticBlipStreamer.setText(+entityId, newEntityData.text);
54 |
55 | if (newEntityData.hasOwnProperty("name"))
56 | staticBlipStreamer.setName(+entityId, newEntityData.name);
57 |
58 | if (newEntityData.hasOwnProperty("name"))
59 | staticBlipStreamer.setSprite(+entityId, newEntityData.sprite);
60 |
61 | if (newEntityData.hasOwnProperty("scale"))
62 | staticBlipStreamer.setScale(+entityId, newEntityData.scale);
63 | }
64 | if (entityType == BLIPTYPE2) {
65 | if (newEntityData.hasOwnProperty("text"))
66 | dynamicBlipStreamer.setText(+entityId, newEntityData.text);
67 |
68 | if (newEntityData.hasOwnProperty("name"))
69 | dynamicBlipStreamer.setName(+entityId, newEntityData.name);
70 |
71 | if (newEntityData.hasOwnProperty("name"))
72 | dynamicBlipStreamer.setSprite(+entityId, newEntityData.sprite);
73 |
74 | if (newEntityData.hasOwnProperty("scale"))
75 | dynamicBlipStreamer.setScale(+entityId, newEntityData.scale);
76 | }
77 | });
78 |
79 | // when a streamed in object needs to be removed
80 | alt.onServer("entitySync:clearCache", (entityId, entityType) => {
81 | if (entityType == BLIPTYPE) {
82 | staticBlipStreamer.removeBlip(+entityId, true);
83 | }else if (entityType == BLIPTYPE2){
84 | dynamicBlipStreamer.removeBlip(+entityId, true);
85 | }
86 | });
87 |
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-marker/client.mjs:
--------------------------------------------------------------------------------
1 | import * as alt from 'alt';
2 | import { markerStreamer } from "./marker-streamer";
3 |
4 | // when an object is streamed in
5 | alt.onServer("entitySync:create", (entityId, entityType, position, currEntityData) => {
6 | if( currEntityData ) {
7 | let data = currEntityData;
8 | if(entityType == 0 ) {
9 | markerStreamer.addMarker(
10 | +entityId, data.markerType, +entityType,
11 | position, data.rotation,
12 | data.direction, data.scale, data.color, data.bobUpDown, data.faceCam, data.rotate, data.textureDict, data.textureName, data.drawOnEnter,
13 | );
14 | }
15 | }
16 | else
17 | {
18 | if (entityType == 0){
19 | markerStreamer.restoreMarker( +entityId );
20 | }
21 | }
22 | } );
23 |
24 | // when an object is streamed out
25 | alt.onServer("entitySync:remove", (entityId, entityType) => {
26 | if (entityType == 0){
27 | markerStreamer.removeMarker( +entityId );
28 | }
29 | } );
30 |
31 | // when a streamed in object changes position data
32 | alt.onServer("entitySync:updatePosition", (entityId, entityType, position) => {
33 | if (entityType == 0){
34 | markerStreamer.setPosition( +entityId, position );
35 | }
36 | } );
37 |
38 | // when a streamed in object changes data
39 | alt.onServer("entitySync:updateData", (entityId, entityType, newEntityData) => {
40 | if (entityType == 0){
41 | if( newEntityData.hasOwnProperty( "rotation" ) )
42 | markerStreamer.setRotation( +entityId, newEntityData.rotation );
43 |
44 | if( newEntityData.hasOwnProperty( "markerType" ) )
45 | markerStreamer.setMarkerType( +entityId, newEntityData.markerType );
46 |
47 | if( newEntityData.hasOwnProperty( "drawOnEnter" ) )
48 | markerStreamer.setDrawOnEnter( +entityId, newEntityData.drawOnEnter );
49 |
50 | if( newEntityData.hasOwnProperty( "textureName" ) )
51 | markerStreamer.setTextureName( +entityId, newEntityData.textureName );
52 |
53 | if( newEntityData.hasOwnProperty( "textureDict" ) )
54 | markerStreamer.setTextureDict( +entityId, newEntityData.textureDict );
55 |
56 | if( newEntityData.hasOwnProperty( "rotate" ) )
57 | markerStreamer.setRotate( +entityId, newEntityData.rotate );
58 |
59 | if( newEntityData.hasOwnProperty( "faceCam" ) )
60 | markerStreamer.setFaceCamera( +entityId, newEntityData.faceCam );
61 |
62 | if( newEntityData.hasOwnProperty( "bobUpDown" ) )
63 | markerStreamer.setBobUpDown( +entityId, newEntityData.bobUpDown );
64 |
65 | if( newEntityData.hasOwnProperty( "color" ) )
66 | markerStreamer.setColor( +entityId, newEntityData.color );
67 |
68 | if( newEntityData.hasOwnProperty( "scale" ) )
69 | markerStreamer.setScale( +entityId, newEntityData.scale );
70 |
71 | if( newEntityData.hasOwnProperty( "direction" ) )
72 | markerStreamer.setDirection( +entityId, newEntityData.direction );
73 | }
74 | } );
75 |
76 | // when a streamed in object needs to be removed
77 | alt.onServer("entitySync:clearCache", (entityId, entityType) => {
78 | if (entityType == 0){
79 | markerStreamer.clearMarker(+entityId);
80 | }
81 | } );
82 |
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-object/client.mjs:
--------------------------------------------------------------------------------
1 | import * as alt from 'alt';
2 |
3 | import { objStreamer } from "./object-streamer";
4 |
5 | // when an object is streamed in
6 | alt.onServer("entitySync:create", (entityId, entityType, position, currEntityData) => {
7 | if( currEntityData ) {
8 | let data = currEntityData;
9 | if(data != undefined ) {
10 | if (entityType == 2){
11 | //alt.log("Object Position " +position);
12 | objStreamer.addObject(
13 | +entityId, data.model, +entityType,
14 | position, data.rotation,
15 | data.lodDistance, data.textureVariation, data.dynamic,
16 | data.visible, data.onFire, data.freeze, data.lightColor
17 | );
18 | }
19 | }
20 | }
21 | // this entity has streamed in before, fetch from cache
22 | else
23 | {
24 | if (entityType == 2){
25 | objStreamer.restoreObject( +entityId );
26 | }
27 | }
28 | } );
29 |
30 | // when an object is streamed out
31 | alt.onServer("entitySync:remove", (entityId, entityType) => {
32 | if (entityType == 2){
33 | objStreamer.removeObject( +entityId );
34 | }
35 | } );
36 |
37 | // when a streamed in object changes position data
38 | alt.onServer("entitySync:updatePosition", (entityId, entityType, position) => {
39 | if (entityType == 2){
40 | objStreamer.setPosition( +entityId, position );
41 | }
42 | } );
43 |
44 | // when a streamed in object changes data
45 | alt.onServer("entitySync:updateData", (entityId, entityType, newEntityData) => {
46 | if (entityType == 2){
47 | if( newEntityData.hasOwnProperty( "rotation" ) )
48 | objStreamer.setRotation( +entityId, newEntityData.rotation );
49 |
50 | if( newEntityData.hasOwnProperty( "velocity" ) )
51 | objStreamer.setVelocity( +entityId, newEntityData.velocity );
52 |
53 | if( newEntityData.hasOwnProperty( "model" ) )
54 | objStreamer.setModel( +entityId, newEntityData.model );
55 |
56 | if( newEntityData.hasOwnProperty( "lodDistance" ) )
57 | objStreamer.setLodDistance( +entityId, newEntityData.lodDistance );
58 |
59 | if( newEntityData.hasOwnProperty( "textureVariation" ) )
60 | objStreamer.setTextureVariation( +entityId, newEntityData.textureVariation );
61 |
62 | if( newEntityData.hasOwnProperty( "dynamic" ) )
63 | objStreamer.setDynamic( +entityId, newEntityData.dynamic );
64 |
65 | if( newEntityData.hasOwnProperty( "visible" ) )
66 | objStreamer.setVisible( +entityId, newEntityData.visible );
67 |
68 | if( newEntityData.hasOwnProperty( "onFire" ) )
69 | objStreamer.setOnFire( +entityId, newEntityData.onFire );
70 |
71 | if( newEntityData.hasOwnProperty( "freeze" ) )
72 | objStreamer.setFrozen( +entityId, newEntityData.freeze );
73 |
74 | if( newEntityData.hasOwnProperty( "lightColor" ) )
75 | objStreamer.setLightColor( +entityId, newEntityData.lightColor );
76 |
77 | if( newEntityData.hasOwnProperty( "slideToPosition" ) )
78 | objStreamer.slideToPosition( +entityId, newEntityData.slideToPosition );
79 |
80 | }
81 | } );
82 |
83 | // when a streamed in object needs to be removed
84 | alt.onServer("entitySync:clearCache", (entityId, entityType) => {
85 | if (entityType == 2){
86 | objStreamer.clearObject( +entityId );
87 | }
88 | } );
89 |
--------------------------------------------------------------------------------
/ServerSide/DoorManager.cs:
--------------------------------------------------------------------------------
1 | using AltV.Net;
2 | using AltV.Net.EntitySync;
3 | using System;
4 | using System.Collections.Concurrent;
5 | using System.Collections.Generic;
6 | using System.Numerics;
7 |
8 | namespace EntityStreamer
9 | {
10 | ///
11 | /// DynamicObject class that stores all data related to a single object
12 | ///
13 | public class NetworkDoor : Entity, IEntity
14 | {
15 | ///
16 | /// Set or get the current door hash.
17 | ///
18 | public uint Hash
19 | {
20 | get
21 | {
22 | if (!TryGetData("hash", out uint hash))
23 | return 0;
24 |
25 | return hash;
26 | }
27 | set
28 | {
29 | if (Hash == value)
30 | return;
31 |
32 | SetData("hash", value);
33 | }
34 | }
35 |
36 | public float Heading
37 | {
38 | get
39 | {
40 | if (!TryGetData("heading", out float heading))
41 | return 0;
42 |
43 | return heading;
44 | }
45 | set
46 | {
47 | if (Math.Abs(Heading - value) < float.Epsilon)
48 | return;
49 |
50 | SetData("heading", value);
51 | }
52 | }
53 |
54 | ///
55 | /// Set the lock statut of the door
56 | ///
57 | public bool? Locked
58 | {
59 | get
60 | {
61 | if (!TryGetData("locked", out bool locked))
62 | return false;
63 |
64 | return locked;
65 | }
66 | set
67 | {
68 | if (value == null)
69 | {
70 | SetData("locked", null);
71 | return;
72 | }
73 | if (Locked == value)
74 | return;
75 |
76 | SetData("locked", value);
77 | }
78 | }
79 |
80 | public NetworkDoor(Vector3 position, int dimension, uint range, ulong entityType) : base(entityType, position, dimension, range)
81 | {
82 | }
83 |
84 | public void Delete()
85 | {
86 | DoorStreamer.DoorList.TryRemove(this.Id, out NetworkDoor value);
87 | AltEntitySync.RemoveEntity(this);
88 | }
89 |
90 | public void Destroy()
91 | {
92 | DoorStreamer.DoorList.TryRemove(this.Id, out NetworkDoor value);
93 | AltEntitySync.RemoveEntity(this);
94 | }
95 | }
96 |
97 | public class DoorStreamer
98 | {
99 | public static ConcurrentDictionary DoorList = new ConcurrentDictionary();
100 |
101 | public static NetworkDoor Create(
102 | uint hash, Vector3 position, float heading ,bool locked, uint streamRange = 25
103 | )
104 | {
105 | NetworkDoor obj = new NetworkDoor(position, 0, streamRange, 6)
106 | {
107 | Hash = hash,
108 | Locked = locked,
109 | Heading = heading,
110 | };
111 | AltEntitySync.AddEntity(obj);
112 | DoorList.TryAdd(obj.Id, obj);
113 | return obj;
114 | }
115 |
116 | public static bool Delete(ulong dynamicObjectId)
117 | {
118 | NetworkDoor obj = GetDoor(dynamicObjectId);
119 |
120 | if (obj == null)
121 | return false;
122 | DoorList.TryRemove(obj.Id, out NetworkDoor value);
123 | AltEntitySync.RemoveEntity(obj);
124 | return true;
125 | }
126 |
127 | public static void Delete(NetworkDoor obj)
128 | {
129 | DoorList.TryRemove(obj.Id, out NetworkDoor value);
130 | AltEntitySync.RemoveEntity(obj);
131 | }
132 |
133 | public static NetworkDoor GetDoor(ulong dynamicObjectId)
134 | {
135 | return DoorList[dynamicObjectId];
136 | }
137 |
138 | public static List GetAllDoors()
139 | {
140 | List objects = new List();
141 |
142 | foreach (KeyValuePair entity in DoorList)
143 | {
144 | objects.Add(entity.Value);
145 | }
146 |
147 | return objects;
148 | }
149 |
150 | public static (NetworkDoor obj, float distance) GetClosestDoor(Vector3 pos)
151 | {
152 | if (GetAllDoors().Count == 0)
153 | return (null, 5000);
154 |
155 | NetworkDoor obj = null;
156 | float distance = 5000;
157 |
158 | foreach (NetworkDoor o in GetAllDoors())
159 | {
160 | float dist = Vector3.Distance(o.Position, pos);
161 | if (dist < distance)
162 | {
163 | obj = o;
164 | distance = dist;
165 | }
166 | }
167 |
168 | return (obj, distance);
169 | }
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-marker/marker-streamer.mjs:
--------------------------------------------------------------------------------
1 | import * as alt from 'alt';
2 | import * as natives from 'natives';
3 |
4 | class MarkerStreamer {
5 | constructor( ) {
6 | this.markers = {};
7 | }
8 |
9 | async addMarker( entityId, markerType, entityType, pos, rot, dir, scale, color, bobUpDown, faceCam, rotate, textureDict, textureName, drawOnEnter ) {
10 | this.removeMarker( +entityId );
11 | this.clearMarker( +entityId );
12 |
13 | let marker = {
14 | onDisplay: true, markerType: markerType,
15 | entityId: +entityId, entityType: +entityType, position: pos
16 | };
17 |
18 | this.markers[entityId] = marker;
19 |
20 | this.setRotation( +entityId, rot );
21 | this.setDirection( +entityId, dir );
22 | this.setScale( +entityId, scale );
23 | this.setColor( +entityId, color );
24 | this.setBobUpDown( +entityId, bobUpDown );
25 | this.setFaceCamera( +entityId, faceCam );
26 | this.setRotate( +entityId, rotate );
27 | this.setTextureDict( +entityId, textureDict );
28 | this.setTextureName( +entityId, textureName );
29 | this.setDrawOnEnter( +entityId, drawOnEnter );
30 | }
31 |
32 | getMarker( entityId ) {
33 | if(this.markers.hasOwnProperty(entityId)){
34 | return this.markers[entityId];
35 | }else{
36 | return null;
37 | }
38 | }
39 |
40 | restoreMarker( entityId ) {
41 | if(this.markers.hasOwnProperty(entityId)){
42 | this.markers[entityId].onDisplay = true;
43 | }
44 | }
45 |
46 |
47 | removeMarker( entityId ) {
48 | if(this.markers.hasOwnProperty(entityId)){
49 | this.markers[entityId].onDisplay = false;
50 | }
51 | }
52 |
53 | clearMarker( entityId ) {
54 | if(this.markers.hasOwnProperty(entityId)){
55 | delete this.markers[entityId];
56 | }
57 | }
58 |
59 | clearAllMarker( ) {
60 | this.markers = {};
61 | }
62 |
63 | setMarkerType( entityId, type = 0 ) {
64 | if(this.markers.hasOwnProperty(entityId)){
65 | this.markers[entityId].markerType = type;
66 | }
67 | }
68 |
69 |
70 | setTextureName( entityId, textureName = undefined ) {
71 | if(this.markers.hasOwnProperty(entityId)){
72 | this.markers[entityId].textureName = textureName;
73 | }
74 | }
75 |
76 | setTextureDict( entityId, textureDict = undefined ) {
77 | if(this.markers.hasOwnProperty(entityId)){
78 | this.markers[entityId].textureDict = textureDict;
79 | }
80 | }
81 |
82 | setDrawOnEnter( entityId, drawOnEnter = false ) {
83 | if(this.markers.hasOwnProperty(entityId)){
84 | this.markers[entityId].drawOnEnter = drawOnEnter;
85 | }
86 | }
87 |
88 | setRotate( entityId, rotate = false ) {
89 | if(this.markers.hasOwnProperty(entityId)){
90 | this.markers[entityId].rotate = rotate;
91 | }
92 | }
93 |
94 | setFaceCamera( entityId, faceCam = false) {
95 | if(this.markers.hasOwnProperty(entityId)){
96 | this.markers[entityId].faceCam = faceCam;
97 | }
98 | }
99 |
100 | setBobUpDown( entityId, bobUpDown = false) {
101 | if(this.markers.hasOwnProperty(entityId)){
102 | this.markers[entityId].bobUpDown = bobUpDown;
103 | }
104 | }
105 |
106 | setColor( entityId, color = { r: 255, g: 255, b: 255, a: 255 } ) {
107 | if(this.markers.hasOwnProperty(entityId)){
108 | this.markers[entityId].color = color;
109 | }
110 | }
111 |
112 | setScale( entityId, scale = { x: 0, y: 0, z: 0 } ) {
113 | if(this.markers.hasOwnProperty(entityId)){
114 | this.markers[entityId].scale = scale;
115 | }
116 | }
117 |
118 | setDirection( entityId, dir = { x: 0, y: 0, z: 0 } ) {
119 | if(this.markers.hasOwnProperty(entityId)){
120 | this.markers[entityId].direction = dir;
121 | }
122 | }
123 |
124 | setRotation( entityId, rot = { x: 0, y: 0, z: 0 } ) {
125 | if(this.markers.hasOwnProperty(entityId)){
126 | this.markers[entityId].rotation = rot;
127 | }
128 | }
129 |
130 | setPosition( entityId, pos = { x: 0, y: 0, z: 0 } ) {
131 | if(this.markers.hasOwnProperty(entityId)){
132 | this.markers[entityId].position = pos;
133 | }
134 | }
135 | }
136 |
137 | export const markerStreamer = new MarkerStreamer();
138 |
139 | alt.on( "resourceStop", ( ) => {
140 | markerStreamer.clearAllMarker();
141 | } );
142 |
143 | alt.everyTick( ( ) => {
144 | for(var key in markerStreamer.markers) {
145 | let marker = markerStreamer.markers[key];
146 | if(marker.onDisplay){
147 | natives.drawMarker(
148 | marker.markerType, marker.position.x, marker.position.y, marker.position.z,
149 | marker.direction.x, marker.direction.y, marker.direction.z,
150 | marker.rotation.x, marker.rotation.y, marker.rotation.z,
151 | marker.scale.x, marker.scale.y, marker.scale.z,
152 | marker.color.r, marker.color.g, marker.color.b, marker.color.a,
153 | !!marker.bobUpDown, !!marker.faceCam, 2, !!marker.rotate, marker.textureDict, marker.textureName, !!marker.drawOnEnter
154 | );
155 | }
156 | }
157 | } );
158 |
--------------------------------------------------------------------------------
/ServerSide/HelpTextManager.cs:
--------------------------------------------------------------------------------
1 | using AltV.Net.Data;
2 | using AltV.Net.EntitySync;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Numerics;
6 |
7 | namespace EntityStreamer
8 | {
9 |
10 | ///
11 | /// HelpText class that stores all data
12 | ///
13 | public class HelpText : Entity, IEntity
14 | {
15 |
16 | ///
17 | /// Set/get the HelpText text.
18 | ///
19 | public string Text
20 | {
21 | get
22 | {
23 | if (!TryGetData("text", out string text))
24 | return null;
25 |
26 | return text;
27 | }
28 | set
29 | {
30 | SetData("text", value);
31 | }
32 | }
33 |
34 | public static object HelpTextLockHandle = new object();
35 | private static List helpTextList = new List();
36 | public static List HelpTextList
37 | {
38 | get
39 | {
40 | lock (HelpTextLockHandle)
41 | {
42 | return helpTextList;
43 | }
44 | }
45 | set
46 | {
47 | helpTextList = value;
48 | }
49 | }
50 |
51 | public HelpText(Vector3 position, int dimension, uint range, ulong entityType) : base(entityType, position, dimension, range)
52 | {
53 | }
54 |
55 | ///
56 | /// Destroy this textlabel.
57 | ///
58 | public void Delete()
59 | {
60 | HelpText.HelpTextList.Remove(this);
61 | AltEntitySync.RemoveEntity(this);
62 | }
63 |
64 | public void SetText(string text)
65 | {
66 | Text = text;
67 | }
68 | }
69 |
70 | public static class HelpTextStreamer
71 | {
72 | ///
73 | /// Create a new HelpText.
74 | ///
75 | /// The text to be displayed.
76 | /// The newly created dynamic textlabel.
77 | public static HelpText Create(
78 | string text, Vector3 position, int dimension = 0, uint streamRange = 5
79 | )
80 | {
81 | HelpText helper = new HelpText(position, dimension, streamRange, 3)
82 | {
83 | Text = text,
84 | };
85 |
86 | HelpText.HelpTextList.Add(helper);
87 | AltEntitySync.AddEntity(helper);
88 | return helper;
89 | }
90 |
91 | ///
92 | /// Destroy HelpText by it's ID.
93 | ///
94 | /// The ID of the text label.
95 | /// True if successful, false otherwise.
96 | public static bool DeleteHelpText(ulong dynamicTextLabelId)
97 | {
98 | HelpText obj = GetHelpText(dynamicTextLabelId);
99 |
100 | if (obj == null)
101 | return false;
102 |
103 | HelpText.HelpTextList.Remove(obj);
104 | AltEntitySync.RemoveEntity(obj);
105 | return true;
106 | }
107 |
108 | ///
109 | /// Destroy an HelpText.
110 | ///
111 | /// The text label instance to destroy.
112 | public static void DeleteHelpText(HelpText dynamicTextLabel)
113 | {
114 | HelpText.HelpTextList.Remove(dynamicTextLabel);
115 | AltEntitySync.RemoveEntity(dynamicTextLabel);
116 | }
117 |
118 | ///
119 | /// Get a HelpText by it's ID.
120 | ///
121 | /// The ID of the textlabel.
122 | /// The dynamic textlabel or null if not found.
123 | public static HelpText GetHelpText(ulong dynamicTextLabelId)
124 | {
125 | if (!AltEntitySync.TryGetEntity(dynamicTextLabelId, 4, out IEntity entity))
126 | {
127 | Console.WriteLine($"[OBJECT-STREAMER] [GetDynamicTextLabel] ERROR: Entity with ID { dynamicTextLabelId } couldn't be found.");
128 | return null;
129 | }
130 |
131 | return (HelpText)entity;
132 | }
133 |
134 | ///
135 | /// Destroy all HelpText.
136 | ///
137 | public static void DeleteAllHelpText()
138 | {
139 | foreach (HelpText obj in GetAllHelpText())
140 | {
141 | HelpText.HelpTextList.Remove(obj);
142 | AltEntitySync.RemoveEntity(obj);
143 | }
144 | }
145 |
146 | ///
147 | /// Get all HelpText.
148 | ///
149 | /// A list of dynamic textlabels.
150 | public static List GetAllHelpText()
151 | {
152 | List textLabels = new List();
153 |
154 | foreach (IEntity entity in HelpText.HelpTextList)
155 | {
156 | HelpText textLabel = GetHelpText(entity.Id);
157 |
158 | if (textLabel != null)
159 | textLabels.Add(textLabel);
160 | }
161 |
162 | return textLabels;
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-text/textlabel-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 | class TextLabelStreamer {
9 | constructor() {
10 | this.textLabels = {};
11 | }
12 |
13 | async addTextLabel( entityId, text, position, scale, font, color, dropShadow, edge, center, proportional, entityType ) {
14 | this.removeTextLabel( +entityId );
15 | this.clearTextLabel( +entityId );
16 |
17 | let textLabel = {
18 | onDisplay: true, position: position,
19 | entityId: +entityId, entityType: +entityType,
20 | };
21 |
22 | this.textLabels[entityId] = textLabel;
23 |
24 | this.setText( +entityId, text );
25 | this.setScale( +entityId, scale );
26 | this.setFont( +entityId, font );
27 | this.setColor( +entityId, color );
28 | this.setDropShadow( +entityId, dropShadow );
29 | this.setEdge( +entityId, edge );
30 | this.setCenter( +entityId, center );
31 | this.setProportional( +entityId, proportional );
32 | }
33 |
34 | getTextLabel( entityId ) {
35 | if(this.textLabels.hasOwnProperty(entityId)){
36 | return this.textLabels[entityId];
37 | }else{
38 | return null;
39 | }
40 | }
41 |
42 | restoreTextLabel( entityId ) {
43 | if(this.textLabels.hasOwnProperty(entityId)){
44 | this.textLabels[entityId].onDisplay = true;
45 | }
46 | }
47 |
48 | removeTextLabel( entityId ) {
49 | if(this.textLabels.hasOwnProperty(entityId)){
50 | this.textLabels[entityId].onDisplay = false;
51 | }
52 | }
53 |
54 | clearTextLabel( entityId ) {
55 | if(this.textLabels.hasOwnProperty(entityId)){
56 | delete this.textLabels[entityId];
57 | }
58 | }
59 |
60 | clearAllTextLabel() {
61 | this.textLabels = {};
62 | }
63 |
64 | setPosition( entityId, pos ) {
65 | if(this.textLabels.hasOwnProperty(entityId)){
66 | this.textLabels[entityId].position = pos;
67 | }
68 | }
69 |
70 | setText( entityId, text = "3D Textlabel" ) {
71 | if(this.textLabels.hasOwnProperty(entityId)){
72 | this.textLabels[entityId].text = text;
73 | }
74 | }
75 |
76 | setScale( entityId, scale = 1 ) {
77 | if(this.textLabels.hasOwnProperty(entityId)){
78 | this.textLabels[entityId].scale = scale;
79 | }
80 | }
81 |
82 | setFont( entityId, font = 4 ) {
83 | if(this.textLabels.hasOwnProperty(entityId)){
84 | this.textLabels[entityId].font = font;
85 | }
86 | }
87 |
88 | setColor( entityId, color = { r: 255, g: 255, b: 255, a: 255 } ) {
89 | if(this.textLabels.hasOwnProperty(entityId)){
90 | this.textLabels[entityId].color = color;
91 | }
92 | }
93 |
94 | setDropShadow( entityId, dropShadow = { distance: 0, r: 0, g: 0, b: 0, a: 255 } ) {
95 | if(this.textLabels.hasOwnProperty(entityId)){
96 | this.textLabels[entityId].dropShadow = dropShadow;
97 | }
98 | }
99 |
100 | setEdge( entityId, edge = { r: 255, g: 255, b: 255, a: 255 } ) {
101 | if(this.textLabels.hasOwnProperty(entityId)){
102 | this.textLabels[entityId].edge = edge;
103 | }
104 | }
105 |
106 | setCenter( entityId, center = true ) {
107 | if(this.textLabels.hasOwnProperty(entityId)){
108 | this.textLabels[entityId].center = center;
109 | }
110 | }
111 |
112 | setProportional( entityId, proportional = true ) {
113 | if(this.textLabels.hasOwnProperty(entityId)){
114 | this.textLabels[entityId].proportional = proportional;
115 | }
116 | }
117 | }
118 |
119 | export const textLabelStreamer = new TextLabelStreamer();
120 |
121 | alt.on( "resourceStop", () => {
122 | textLabelStreamer.clearAllTextLabel();
123 | } );
124 |
125 | function draw3dText( text, pos, scale, font, color, dropShadow, edge, center, proportional ) {
126 |
127 | const localPlayer = alt.Player.local;
128 | const entity = localPlayer.vehicle ? localPlayer.vehicle.scriptID : localPlayer.scriptID;
129 | const vector = natives.getEntityVelocity(entity);
130 | const frameTime = natives.getFrameTime();
131 | natives.setDrawOrigin(pos.x + (vector.x * frameTime), pos.y + (vector.y * frameTime), pos.z + (vector.z * frameTime), 0);
132 | natives.beginTextCommandDisplayText( 'STRING' );
133 | natives.addTextComponentSubstringPlayerName( text );
134 | natives.setTextFont( font );
135 | var size = scale / 2.3;
136 | natives.setTextScale( 1, size );
137 | natives.setTextWrap( 0.0, 1.0 );
138 | natives.setTextCentre( center );
139 | natives.setTextProportional( proportional );
140 |
141 | if(!color || !color.r)
142 | {
143 | color = { r: 0, g:0,b:0,a:0};
144 | }
145 | natives.setTextColour( color.r, color.g, color.b, color.a );
146 | natives.setTextOutline();
147 | natives.setTextDropShadow();
148 |
149 | natives.endTextCommandDisplayText( 0, 0, 0 );
150 | natives.clearDrawOrigin();
151 | }
152 |
153 | alt.everyTick( () => {
154 | for(var key in textLabelStreamer.textLabels) {
155 | let textLabel = textLabelStreamer.textLabels[key];
156 | if(textLabel.onDisplay){
157 | draw3dText(
158 | textLabel.text,
159 | textLabel.position,
160 | textLabel.scale,
161 | textLabel.font,
162 | textLabel.color,
163 | textLabel.dropShadow,
164 | textLabel.edge,
165 | textLabel.center,
166 | textLabel.proportional
167 | );
168 | }
169 | }
170 | } );
171 |
--------------------------------------------------------------------------------
/ServerSide/BlipManager.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 System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Numerics;
10 | using System.Runtime.CompilerServices;
11 | using System.Threading.Tasks;
12 |
13 | namespace EntityStreamer
14 | {
15 | ///
16 | /// Blip class that stores all data related to a single blip.
17 | ///
18 | public class Blip : AltV.Net.EntitySync.Entity, AltV.Net.EntitySync.IEntity
19 | {
20 | ///
21 | /// The text to display on the blip in the map menu
22 | ///
23 | public string Name
24 | {
25 | get
26 | {
27 | if (!TryGetData("name", out string name))
28 | return null;
29 |
30 | return name;
31 | }
32 | set
33 | {
34 | SetData("name", value);
35 | }
36 | }
37 |
38 | ///
39 | /// ID of the sprite to use, can be found on the ALTV wiki
40 | ///
41 | public int Sprite
42 | {
43 | get
44 | {
45 | if (!TryGetData("sprite", out int spriteId))
46 | return 0;
47 |
48 | return spriteId;
49 | }
50 | set
51 | {
52 | SetData("sprite", value);
53 | }
54 | }
55 |
56 | ///
57 | /// Blip Color code, can also be found on the ALTV wiki
58 | ///
59 | public int Color
60 | {
61 | get
62 | {
63 | if (!TryGetData("color", out int color))
64 | return 0;
65 |
66 | return color;
67 | }
68 | set
69 | {
70 | SetData("color", value);
71 | }
72 | }
73 |
74 | ///
75 | /// Scale of the blip, 1 is regular size.
76 | ///
77 | public float Scale
78 | {
79 | get
80 | {
81 | if (!TryGetData("scale", out float scale))
82 | return 1;
83 |
84 | return scale;
85 | }
86 | set
87 | {
88 | SetData("scale", value);
89 | }
90 | }
91 |
92 | ///
93 | /// Whether this blip can be seen on the minimap from anywhere on the map, or only when close to it(it will always show on the main map).
94 | ///
95 | public bool ShortRange
96 | {
97 | get
98 | {
99 | if (!TryGetData("shortRange", out bool shortRange))
100 | return true;
101 |
102 | return shortRange;
103 | }
104 | set
105 | {
106 | SetData("shortRange", value);
107 | }
108 | }
109 |
110 | public Blip(Vector3 position, int dimension, uint range, ulong entityType) : base(entityType, position, dimension, range)
111 | {
112 | }
113 |
114 | public bool IsStaticBlip { get; set; }
115 |
116 | ///
117 | /// Destroy this blip.
118 | ///
119 | public void Delete()
120 | {
121 | AltEntitySync.RemoveEntity(this);
122 | }
123 |
124 | public void SetPosition(Position pos)
125 | {
126 | this.Position = pos;
127 | }
128 |
129 | public void SetBlipType(int type)
130 | {
131 | //TODO Transformer in runtime un blip static en dynamique et inversement.
132 | }
133 | }
134 |
135 | public static class BlipStreamer
136 | {
137 | public static Dictionary BlipList = new Dictionary();
138 |
139 | ///
140 | /// Create static blip without any range limit
141 | ///
142 | ///
143 | ///
144 | ///
145 | ///
146 | ///
147 | ///
148 | ///
149 | ///
150 | ///
151 | public static Blip CreateStaticBlip(string name, int color, float scale, bool shortRange, int spriteId, Vector3 position, int dimension, uint range = 100)
152 | {
153 | Blip blip = new Blip(position, dimension, range, 4)
154 | {
155 | Color = color,
156 | Scale = scale,
157 | ShortRange = shortRange,
158 | Sprite = spriteId,
159 | Name = name,
160 | IsStaticBlip = true
161 | };
162 | AltEntitySync.AddEntity(blip);
163 | BlipList.Add(blip.Id, blip);
164 | return blip;
165 | }
166 |
167 | ///
168 | /// Create Dynamic Blip.
169 | ///
170 | ///
171 | ///
172 | ///
173 | ///
174 | ///
175 | ///
176 | ///
177 | ///
178 | ///
179 | public static Blip CreateDynamicBlip(string name, int color, float scale, bool shortRange, int spriteId, Vector3 position, int dimension, uint range = 200)
180 | {
181 | Blip blip = new Blip(position, dimension, range, 5)
182 | {
183 | Color = color,
184 | Scale = scale,
185 | ShortRange = shortRange,
186 | Sprite = spriteId,
187 | Name = name,
188 | IsStaticBlip = false,
189 | };
190 | AltEntitySync.AddEntity(blip);
191 | BlipList.Add(blip.Id, blip);
192 | return blip;
193 | }
194 |
195 | ///
196 | /// Destroy a dynamic blip
197 | ///
198 | /// The blip to destroy
199 | public static void DestroyBlip(Blip blip)
200 | {
201 | BlipList.Remove(blip.Id);
202 | AltEntitySync.RemoveEntity(blip);
203 | }
204 |
205 | public static Blip GetBlip(ulong dynamicObjectId)
206 | {
207 |
208 | return BlipList[dynamicObjectId];
209 | }
210 |
211 | public static List GetAllBlip()
212 | {
213 | List objects = new List();
214 |
215 | foreach (KeyValuePair entity in BlipStreamer.BlipList)
216 | {
217 | Blip obj = GetBlip(entity.Key);
218 |
219 | if (obj != null)
220 | objects.Add(obj);
221 | }
222 |
223 | return objects;
224 | }
225 |
226 | public static (Blip obj, float distance) GetClosestBlip(Vector3 pos)
227 | {
228 | if (GetAllBlip().Count == 0)
229 | return (null, 5000);
230 |
231 | Blip obj = null;
232 | float distance = 5000;
233 |
234 | foreach (Blip o in GetAllBlip())
235 | {
236 | float dist = Vector3.Distance(o.Position, pos);
237 | if (dist < distance)
238 | {
239 | obj = o;
240 | distance = dist;
241 | }
242 | }
243 |
244 | return (obj, distance);
245 | }
246 | }
247 | }
248 |
249 |
--------------------------------------------------------------------------------
/ClientSide/resources/sarp-streamer-object/object-streamer.mjs:
--------------------------------------------------------------------------------
1 | import * as alt from 'alt';
2 | import * as natives from 'natives';
3 |
4 | function waitFor(duration) {
5 | return new Promise(resolve => setTimeout(resolve, duration));
6 | }
7 |
8 | async function loadModel(model) {
9 | if (typeof model === "string") model = alt.hash(model);
10 | if (!natives.isModelValid(model)) return false;
11 | if(natives.hasModelLoaded(model)) return true;
12 |
13 | natives.requestModel(model);
14 |
15 | let hasModelLoaded = false;
16 | let tries = 0;
17 |
18 | while (!(hasModelLoaded = natives.hasModelLoaded(model)) && tries++ < 10) {
19 | await waitFor(10);
20 | }
21 |
22 | return hasModelLoaded;
23 | }
24 |
25 | class ObjectStreamer {
26 | constructor( ) {
27 | this.objects = {};
28 | }
29 |
30 | async addObject(entityId, model, entityType, pos, rot, lodDistance, textureVariation, dynamic, visible, onFire, frozen, lightColor ) {
31 | // clear the object incase it still exists.
32 | this.removeObject( +entityId );
33 | this.clearObject( +entityId );
34 |
35 | loadModel(model).then(() =>
36 | {
37 | let handle = natives.createObjectNoOffset(model, pos.x, pos.y, pos.z, false, false, false );
38 | let obj = { handle: handle, entityId: entityId, model: model, entityType: entityType, position: pos, frozen: frozen };
39 | this.objects[entityId] = obj;
40 | this.setRotation( +entityId, rot );
41 | this.setLodDistance( obj, lodDistance );
42 | this.setTextureVariation( +entityId, textureVariation );
43 | this.setDynamic( +entityId, dynamic );
44 | this.setVisible( obj, visible );
45 | this.setOnFire( +entityId, onFire );
46 | this.setFrozen( +entityId, frozen );
47 | this.setLightColor( +entityId, lightColor );
48 | });
49 | }
50 |
51 | getObject( entityId ) {
52 | if(this.objects.hasOwnProperty(entityId)){
53 | return this.objects[entityId];
54 | }else{
55 | return null;
56 | }
57 | }
58 |
59 | async restoreObject( entityId ) {
60 | if(this.objects.hasOwnProperty(entityId)){
61 | let obj = this.objects[entityId];
62 | loadModel(obj.model).then(() =>
63 | {
64 | this.objects[entityId].handle = natives.createObjectNoOffset( alt.hash( obj.model ), obj.position.x, obj.position.y, obj.position.z, false, false, false );
65 | this.setRotation( +entityId, obj.rotation );
66 | this.setLodDistance( obj, obj.lodDistance );
67 | this.setTextureVariation( +entityId, obj.textureVariation );
68 | this.setDynamic( +entityId, obj.dynamic );
69 | this.setVisible( obj, obj.visible );
70 | this.setOnFire( +entityId, obj.onFire );
71 | this.setFrozen( +entityId, obj.frozen );
72 | this.setLightColor( +entityId, obj.lightColor );
73 | });
74 | }
75 | }
76 |
77 | removeObject( entityId ) {
78 | if(this.objects.hasOwnProperty(entityId)){
79 | natives.deleteObject( this.objects[entityId].handle );
80 | this.objects[entityId].handle = null;
81 | }
82 | }
83 |
84 | clearObject( entityId ) {
85 | if(this.objects.hasOwnProperty(entityId)){
86 | delete this.objects[entityId];
87 | }
88 | }
89 |
90 | clearAllObject() {
91 | this.objects= {};
92 | }
93 |
94 | setRotation( entityId, rot ) {
95 | if(this.objects.hasOwnProperty(entityId)){
96 | natives.setEntityRotation( this.objects[entityId].handle, rot.x, rot.y, rot.z, 0, true );
97 | this.objects[entityId].rotation = rot;
98 | }
99 | }
100 | setVelocity( entityId, vel ) {
101 | if(this.objects.hasOwnProperty(entityId)){
102 | natives.setEntityVelocity( this.objects[entityId].handle, vel.x, vel.y, vel.z);
103 | this.objects[entityId].velocity = vel;
104 | }
105 | }
106 | slideToPosition( entityId, pos ) {
107 | natives.slideObject(this.objects[entityId].handle, pos.x, pos.y, pos.z, 8, 8, 8, true);
108 | }
109 |
110 | setPosition( entityId, pos ) {
111 | if(this.objects.hasOwnProperty(entityId)){
112 | natives.setEntityCoordsNoOffset( this.objects[entityId].handle, pos.x, pos.y, pos.z, true, true, true );
113 | this.objects[entityId].position = pos;
114 | }
115 | }
116 |
117 | async setModel( entityId, model ) {
118 | if(this.objects.hasOwnProperty(entityId)){
119 | this.objects[entityId].model = model;
120 | }
121 | }
122 |
123 | setLodDistance( entityId, lodDistance ) {
124 | if(this.objects.hasOwnProperty(entityId) && lodDistance !== null){
125 | natives.setEntityLodDist( this.objects[entityId].handle, lodDistance );
126 | this.objects[entityId].lodDistance = lodDistance;
127 | }
128 | }
129 |
130 | setTextureVariation( entityId, textureVariation = null ) {
131 | if(this.objects.hasOwnProperty(entityId)){
132 | if(textureVariation == null)
133 | natives.setObjectTextureVariation( this.objects[entityId].handle, textureVariation );
134 | this.objects[entityId].textureVariation = textureVariation;
135 | }
136 | }
137 |
138 | setDynamic( entityId, dynamic ) {
139 | if(this.objects.hasOwnProperty(entityId) && dynamic !== null){
140 | natives.setEntityDynamic( this.objects[entityId].handle, !!dynamic );
141 | this.objects[entityId].dynamic = !!dynamic;
142 | }
143 | }
144 |
145 | setVisible( entityId, visible ) {
146 | if(this.objects.hasOwnProperty(entityId) && visible !== null){
147 | natives.setEntityVisible( this.objects[entityId].handle, !!visible, false );
148 | this.objects[entityId].visible = !!visible;
149 | }
150 | }
151 |
152 | setOnFire( entityId, onFire = null ) {
153 | if(this.objects.hasOwnProperty(entityId) && onFire !== null){
154 | if( !!onFire )
155 | {
156 | this.objects[entityId].fireHandle = natives.startScriptFire( this.objects[entityId].position.x, this.objects[entityId].position.y, this.objects[entityId].position.z, 1, true );
157 | }
158 | else
159 | {
160 | if( this.objects[entityId].fireHandle !== null )
161 | {
162 | natives.removeScriptFire( this.objects[entityId].fireHandle );
163 | this.objects[entityId].fireHandle = null;
164 | }
165 | }
166 |
167 | this.objects[entityId].onFire = !!onFire;
168 | }
169 | }
170 |
171 | setFrozen( entityId, frozen ) {
172 | if(this.objects.hasOwnProperty(entityId) && frozen !== null){
173 | natives.freezeEntityPosition( this.objects[entityId].handle, frozen );
174 | this.objects[entityId].frozen = frozen;
175 | }
176 | }
177 |
178 | setLightColor( entityId, lightColor = {r:0,g:0,b:0} ) {
179 | if(this.objects.hasOwnProperty(entityId) && lightColor !== null){
180 | natives.setObjectLightColor( this.objects[entityId].handle, true, lightColor.r, lightColor.g, lightColor.b );
181 | this.objects[entityId].lightColor = lightColor;
182 | }else{
183 | natives.setObjectLightColor( this.objects[entityId].handle, true, 0, 0, 0 );
184 | this.objects[entityId].lightColor = lightColor;
185 | }
186 | }
187 | }
188 |
189 | export const objStreamer = new ObjectStreamer();
190 |
191 | alt.on( "resourceStop", ( ) => {
192 | objStreamer.clearAllObject();
193 | } );
194 |
--------------------------------------------------------------------------------
/ServerSide/PlayerLabelManager.cs:
--------------------------------------------------------------------------------
1 | using AltV.Net.Data;
2 | using AltV.Net.EntitySync;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Numerics;
6 |
7 | namespace EntityStreamer
8 | {
9 | ///
10 | /// Class to hold drop shadow data.
11 | ///
12 | public class DropShadow
13 | {
14 | public int Distance { get; set; }
15 | public int R { get; set; }
16 | public int G { get; set; }
17 | public int B { get; set; }
18 | public int A { get; set; }
19 | }
20 |
21 | ///
22 | /// DynamicTextLabel class that stores all data related to a single textlabel
23 | ///
24 | public class PlayerLabel : Entity, IEntity
25 | {
26 | private ulong EntityType
27 | {
28 | get
29 | {
30 | if (!TryGetData("entityType", out ulong type))
31 | return 999;
32 |
33 | return type;
34 | }
35 | set
36 | {
37 | // No data changed
38 | if (EntityType == value)
39 | return;
40 |
41 | SetData("entityType", value);
42 | }
43 | }
44 |
45 | ///
46 | /// Set/get or get the current textlabel's scale.
47 | ///
48 | public float? Scale
49 | {
50 | get
51 | {
52 | if (!TryGetData("scale", out float scale))
53 | return null;
54 |
55 | return scale;
56 | }
57 | set
58 | {
59 | SetData("scale", value);
60 | }
61 | }
62 |
63 | ///
64 | /// Set/get the textlabel's text.
65 | ///
66 | public string Text
67 | {
68 | get
69 | {
70 | if (!TryGetData("text", out string text))
71 | return null;
72 |
73 | return text;
74 | }
75 | set
76 | {
77 | SetData("text", value);
78 | }
79 | }
80 |
81 | ///
82 | /// Set/get textlabel center, if true the textlabel will be centered.
83 | ///
84 | public bool Center
85 | {
86 | get
87 | {
88 | if (!TryGetData("center", out bool center))
89 | return default;
90 |
91 | return center;
92 | }
93 | set
94 | {
95 | SetData("center", value);
96 | }
97 | }
98 |
99 | ///
100 | /// Set/get textlabel proportional.
101 | ///
102 | public bool Proportional
103 | {
104 | get
105 | {
106 | if (!TryGetData("proportional", out bool proportional))
107 | return default;
108 |
109 | return proportional;
110 | }
111 | set
112 | {
113 | SetData("proportional", value);
114 | }
115 | }
116 |
117 | ///
118 | /// Set/get textlabel's color.
119 | ///
120 | public Rgba Color
121 | {
122 | get
123 | {
124 | if (!TryGetData("color", out Dictionary data))
125 | return default;
126 |
127 | return new Rgba()
128 | {
129 | R = Convert.ToByte(data["r"]),
130 | G = Convert.ToByte(data["g"]),
131 | B = Convert.ToByte(data["b"]),
132 | A = Convert.ToByte(data["a"]),
133 | };
134 | }
135 | set
136 | {
137 | // No data changed
138 | if (Color.R == value.R && Color.G == value.G && Color.B == value.B && Color.A == value.A)
139 | return;
140 |
141 | Dictionary dict = new Dictionary()
142 | {
143 | ["r"] = Convert.ToInt32(value.R),
144 | ["g"] = Convert.ToInt32(value.G),
145 | ["b"] = Convert.ToInt32(value.B),
146 | ["a"] = Convert.ToInt32(value.A),
147 | };
148 | SetData("color", dict);
149 | }
150 | }
151 |
152 | ///
153 | /// Set/get textlabel's edge color.
154 | ///
155 | public Rgba Edge
156 | {
157 | get
158 | {
159 | if (!TryGetData("edge", out Dictionary data))
160 | return default;
161 |
162 | return new Rgba()
163 | {
164 | R = Convert.ToByte(data["r"]),
165 | G = Convert.ToByte(data["g"]),
166 | B = Convert.ToByte(data["b"]),
167 | A = Convert.ToByte(data["a"]),
168 | };
169 | }
170 | set
171 | {
172 | // No data changed
173 | if (Edge.R == value.R && Edge.G == value.G && Edge.B == value.B && Edge.A == value.A)
174 | return;
175 |
176 | Dictionary dict = new Dictionary()
177 | {
178 | ["r"] = Convert.ToInt32(value.R),
179 | ["g"] = Convert.ToInt32(value.G),
180 | ["b"] = Convert.ToInt32(value.B),
181 | ["a"] = Convert.ToInt32(value.A),
182 | };
183 | SetData("edge", dict);
184 | }
185 | }
186 |
187 | ///
188 | /// Set/get textlabel's drop shadow.
189 | ///
190 | public DropShadow DropShadow
191 | {
192 | get
193 | {
194 | if (!TryGetData("dropShadow", out Dictionary data))
195 | return null;
196 |
197 | return new DropShadow()
198 | {
199 | Distance = Convert.ToInt32(data["distance"]),
200 | R = Convert.ToInt32(data["r"]),
201 | G = Convert.ToInt32(data["g"]),
202 | B = Convert.ToInt32(data["b"]),
203 | A = Convert.ToInt32(data["a"]),
204 | };
205 | }
206 | set
207 | {
208 | // No data changed
209 | if (DropShadow.Distance == value.Distance && DropShadow.R == value.R && DropShadow.G == value.G && DropShadow.B == value.B && DropShadow.A == value.A)
210 | return;
211 |
212 | Dictionary dict = new Dictionary()
213 | {
214 | ["distance"] = value.Distance,
215 | ["r"] = value.R,
216 | ["g"] = value.G,
217 | ["b"] = value.B,
218 | ["a"] = value.A,
219 | };
220 | SetData("dropShadow", dict);
221 | }
222 | }
223 |
224 | ///
225 | /// Set/get textlabel's font type.
226 | ///
227 | public int Font
228 | {
229 | get
230 | {
231 | if (!TryGetData("font", out int font))
232 | return default;
233 |
234 | return font;
235 | }
236 | set
237 | {
238 | SetData("font", value);
239 | }
240 | }
241 |
242 | public static object LabelLockHandle = new object();
243 |
244 | private static List labelList = new List();
245 |
246 | public static List LabelList
247 | {
248 | get
249 | {
250 | lock (LabelLockHandle)
251 | {
252 | return labelList;
253 | }
254 | }
255 | set
256 | {
257 | labelList = value;
258 | }
259 | }
260 |
261 | public PlayerLabel(Vector3 position, int dimension, uint range, ulong entityType) : base(entityType, position, dimension, range)
262 | {
263 | EntityType = entityType;
264 | }
265 |
266 | ///
267 | /// Destroy this textlabel.
268 | ///
269 | public void Delete()
270 | {
271 | PlayerLabel.LabelList.Remove(this);
272 | AltEntitySync.RemoveEntity(this);
273 | }
274 |
275 | public void SetText(string text)
276 | {
277 | Text = text;
278 | }
279 | }
280 |
281 | public static class TextLabelStreamer
282 | {
283 | ///
284 | /// Create a new dynamic textlabel.
285 | ///
286 | /// The text to be displayed.
287 | /// The position to spawn it at.
288 | /// The dimension to spawn it in.
289 | /// Center the textlabel.
290 | /// The color of the textlabel.
291 | /// The scale of the textlabel.
292 | /// The drop shadow of the textlabel.
293 | /// The edge color of the textlabel.
294 | /// The font type of the textlabel.
295 | /// Whether to set textlabel proportional.
296 | /// Stream range, default is 30.
297 | /// The newly created dynamic textlabel.
298 | public static PlayerLabel Create(
299 | string text, Vector3 position, int dimension = 0, bool? center = true, Rgba? color = null, float? scale = 0.7f,
300 | DropShadow dropShadow = null, Rgba? edge = null, int? font = null, bool? proportional = null, uint streamRange = 45
301 | )
302 | {
303 | PlayerLabel textLabel = new PlayerLabel(position, dimension, streamRange, 1)
304 | {
305 | Center = center ?? true,
306 | Color = color ?? new Rgba(255, 255, 255, 255),
307 | DropShadow = dropShadow ?? new DropShadow { Distance = 0, R = 0, G = 0, B = 0, A = 255 },
308 | Edge = edge ?? new Rgba(0, 0, 0, 150),
309 | Font = font ?? 4,
310 | Text = text,
311 | Proportional = proportional ?? true,
312 | Scale = scale
313 | };
314 | PlayerLabel.LabelList.Add(textLabel);
315 | AltEntitySync.AddEntity(textLabel);
316 | return textLabel;
317 | }
318 |
319 | ///
320 | /// Destroy a dynamic text label by it's ID.
321 | ///
322 | /// The ID of the text label.
323 | /// True if successful, false otherwise.
324 | public static bool DestroyDynamicTextLabel(ulong dynamicTextLabelId)
325 | {
326 | PlayerLabel obj = GetDynamicTextLabel(dynamicTextLabelId);
327 |
328 | if (obj == null)
329 | return false;
330 |
331 | PlayerLabel.LabelList.Remove(obj);
332 | AltEntitySync.RemoveEntity(obj);
333 | return true;
334 | }
335 |
336 | ///
337 | /// Destroy a dynamic text label.
338 | ///
339 | /// The text label instance to destroy.
340 | public static void DestroyDynamicTextLabel(PlayerLabel dynamicTextLabel)
341 | {
342 | PlayerLabel.LabelList.Remove(dynamicTextLabel);
343 | AltEntitySync.RemoveEntity(dynamicTextLabel);
344 | }
345 |
346 | ///
347 | /// Get a dynamic text label by it's ID.
348 | ///
349 | /// The ID of the textlabel.
350 | /// The dynamic textlabel or null if not found.
351 | public static PlayerLabel GetDynamicTextLabel(ulong dynamicTextLabelId)
352 | {
353 | if (!AltEntitySync.TryGetEntity(dynamicTextLabelId, 1, out IEntity entity))
354 | {
355 | Console.WriteLine($"[OBJECT-STREAMER] [GetDynamicTextLabel] ERROR: Entity with ID { dynamicTextLabelId } couldn't be found.");
356 | return null;
357 | }
358 |
359 | return (PlayerLabel)entity;
360 | }
361 |
362 | ///
363 | /// Destroy all created dynamic textlabels.
364 | ///
365 | public static void DestroyAllDynamicTextLabels()
366 | {
367 | foreach (PlayerLabel obj in GetAllDynamicTextLabels())
368 | {
369 | AltEntitySync.RemoveEntity(obj);
370 | }
371 | PlayerLabel.LabelList.Clear();
372 | }
373 |
374 | ///
375 | /// Get all created dynamic textlabels.
376 | ///
377 | /// A list of dynamic textlabels.
378 | public static List GetAllDynamicTextLabels()
379 | {
380 | List textLabels = new List();
381 |
382 | foreach (IEntity entity in PlayerLabel.LabelList)
383 | {
384 | PlayerLabel textLabel = GetDynamicTextLabel(entity.Id);
385 |
386 | if (textLabel != null)
387 | textLabels.Add(textLabel);
388 | }
389 |
390 | return textLabels;
391 | }
392 | }
393 | }
--------------------------------------------------------------------------------
/ServerSide/PropManager.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 EntityStreamer
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 |
36 | public void OnWrite(IMValueWriter writer)
37 | {
38 | writer.BeginObject();
39 | writer.Name("X");
40 | writer.Value(X);
41 | writer.Name("Y");
42 | writer.Value(Y);
43 | writer.Name("Z");
44 | writer.Value(Z);
45 | writer.Name("Speed");
46 | writer.Value(Speed);
47 | writer.EndObject();
48 | }
49 | }
50 |
51 | public class Rgb : IWritable
52 | {
53 | public int Red { get; set; }
54 | public int Green { get; set; }
55 | public int Blue { get; set; }
56 |
57 | public Rgb(int red, int green, int blue)
58 | {
59 | Red = red;
60 | Green = green;
61 | Blue = blue;
62 | }
63 |
64 | public void OnWrite(IMValueWriter writer)
65 | {
66 | writer.BeginObject();
67 | writer.Name("Red");
68 | writer.Value(Red);
69 | writer.Name("Green");
70 | writer.Value(Green);
71 | writer.Name("Blue");
72 | writer.Value(Blue);
73 | writer.EndObject();
74 | }
75 | }
76 |
77 | ///
78 | /// DynamicObject class that stores all data related to a single object
79 | ///
80 | public class Prop : Entity, IEntity
81 | {
82 | private static List propList = new List();
83 |
84 | public static List PropList
85 | {
86 | get
87 | {
88 | lock (propList)
89 | {
90 | return propList;
91 | }
92 | }
93 | set
94 | {
95 | propList = value;
96 | }
97 | }
98 |
99 | public AltV.Net.Elements.Entities.IColShape colshape { get; set; }
100 |
101 | ///
102 | /// Set or get the current object's rotation (in degrees).
103 | ///
104 | public Vector3 Rotation
105 | {
106 | get
107 | {
108 | if (!TryGetData("rotation", out Dictionary data))
109 | return default;
110 |
111 | return new Vector3()
112 | {
113 | X = Convert.ToSingle(data["x"]),
114 | Y = Convert.ToSingle(data["y"]),
115 | Z = Convert.ToSingle(data["z"]),
116 | };
117 | }
118 | set
119 | {
120 | // No data changed
121 | if (Rotation.X == value.X && Rotation.Y == value.Y && Rotation.Z == value.Z && value != new Vector3(0, 0, 0))
122 | return;
123 |
124 | Dictionary dict = new Dictionary()
125 | {
126 | ["x"] = value.X,
127 | ["y"] = value.Y,
128 | ["z"] = value.Z,
129 | };
130 | SetData("rotation", dict);
131 | }
132 | }
133 |
134 | public Vector3 Velocity
135 | {
136 | get
137 | {
138 | if (!TryGetData("velocity", out Dictionary data))
139 | return default;
140 |
141 | return new Vector3()
142 | {
143 | X = Convert.ToSingle(data["x"]),
144 | Y = Convert.ToSingle(data["y"]),
145 | Z = Convert.ToSingle(data["z"]),
146 | };
147 | }
148 | set
149 | {
150 | // No data changed
151 | if (Velocity.X == value.X && Velocity.Y == value.Y && Velocity.Z == value.Z && value != new Vector3(0, 0, 0))
152 | return;
153 |
154 | Dictionary dict = new Dictionary()
155 | {
156 | ["x"] = value.X,
157 | ["y"] = value.Y,
158 | ["z"] = value.Z,
159 | };
160 | SetData("velocity", dict);
161 | }
162 | }
163 |
164 | public Vector3 SlideToPosition
165 | {
166 | get
167 | {
168 | if (!TryGetData("SlideToPosition", out Dictionary data))
169 | return default;
170 |
171 | return new Vector3()
172 | {
173 | X = Convert.ToSingle(data["x"]),
174 | Y = Convert.ToSingle(data["y"]),
175 | Z = Convert.ToSingle(data["z"]),
176 | };
177 | }
178 | set
179 | {
180 | // No data changed
181 |
182 | Dictionary dict = new Dictionary()
183 | {
184 | ["x"] = value.X,
185 | ["y"] = value.Y,
186 | ["z"] = value.Z,
187 | };
188 | //Log.Important("SetData SlideToPosition ");
189 | SetData("SlideToPosition", dict);
190 | }
191 | }
192 |
193 | ///
194 | /// Set or get the current object's model.
195 | ///
196 | public uint? Model
197 | {
198 | get
199 | {
200 | if (!TryGetData("model", out uint model))
201 | return null;
202 |
203 | return model;
204 | }
205 | set
206 | {
207 | // No data changed
208 | if (Model == value)
209 | return;
210 |
211 | SetData("model", value);
212 | }
213 | }
214 |
215 | ///
216 | /// Set or get LOD Distance of the object.
217 | ///
218 | public uint? LodDistance
219 | {
220 | get
221 | {
222 | if (!TryGetData("lodDistance", out uint lodDist))
223 | return null;
224 |
225 | return lodDist;
226 | }
227 | set
228 | {
229 | // if value is set to null, reset the data
230 | if (value == null)
231 | {
232 | SetData("lodDistance", null);
233 | return;
234 | }
235 |
236 | // No data changed
237 | if (LodDistance == value)
238 | return;
239 |
240 | SetData("lodDistance", value);
241 | }
242 | }
243 |
244 | ///
245 | /// Get or set the current texture variation, use null to reset it to default.
246 | ///
247 | public TextureVariation? TextureVariation
248 | {
249 | get
250 | {
251 | if (!TryGetData("textureVariation", out int variation))
252 | return null;
253 |
254 | return (TextureVariation)variation;
255 | }
256 | set
257 | {
258 | // if value is set to null, reset the data
259 | if (value == null)
260 | {
261 | SetData("textureVariation", null);
262 | return;
263 | }
264 |
265 | // No data changed
266 | if (TextureVariation == value)
267 | return;
268 |
269 | SetData("textureVariation", (int)value);
270 | }
271 | }
272 |
273 | ///
274 | /// Get or set the object's dynamic state. Some objects can be moved around by the player when dynamic is set to true.
275 | ///
276 | public bool? Dynamic
277 | {
278 | get
279 | {
280 | if (!TryGetData("dynamic", out bool isDynamic))
281 | return false;
282 |
283 | return isDynamic;
284 | }
285 | set
286 | {
287 | // if value is set to null, reset the data
288 | if (value == null)
289 | {
290 | SetData("dynamic", null);
291 | return;
292 | }
293 |
294 | // No data changed
295 | if (Dynamic == value)
296 | return;
297 |
298 | SetData("dynamic", value);
299 | }
300 | }
301 |
302 | ///
303 | /// Set/get visibility state of object
304 | ///
305 | public bool? Visible
306 | {
307 | get
308 | {
309 | if (!TryGetData("visible", out bool visible))
310 | return false;
311 |
312 | return visible;
313 | }
314 | set
315 | {
316 | // if value is set to null, reset the data
317 | if (value == null)
318 | {
319 | SetData("visible", null);
320 | return;
321 | }
322 |
323 | // No data changed
324 | if (Visible == value)
325 | return;
326 |
327 | SetData("visible", value);
328 | }
329 | }
330 |
331 | ///
332 | /// Set/get an object on fire, NOTE: does not work very well as of right now, fire is very small.
333 | ///
334 | public bool? OnFire
335 | {
336 | get
337 | {
338 | if (!TryGetData("onFire", out bool onFire))
339 | return false;
340 |
341 | return onFire;
342 | }
343 | set
344 | {
345 | // if value is set to null, reset the data
346 | if (value == null)
347 | {
348 | SetData("onFire", null);
349 | return;
350 | }
351 |
352 | // No data changed
353 | if (OnFire == value)
354 | return;
355 |
356 | SetData("onFire", value);
357 | }
358 | }
359 |
360 | ///
361 | /// Freeze an object into it's current position. or get it's status
362 | ///
363 | public bool? Freeze
364 | {
365 | get
366 | {
367 | if (!TryGetData("freeze", out bool frozen))
368 | return false;
369 |
370 | return frozen;
371 | }
372 | set
373 | {
374 | // if value is set to null, reset the data
375 | if (value == null)
376 | {
377 | SetData("freeze", null);
378 | return;
379 | }
380 |
381 | // No data changed
382 | if (Freeze == value)
383 | return;
384 |
385 | SetData("freeze", value);
386 | }
387 | }
388 |
389 | ///
390 | /// Set the light color of the object, use null to reset it to default.
391 | ///
392 | public Rgb LightColor
393 | {
394 | get
395 | {
396 | if (!TryGetData("lightColor", out Dictionary data))
397 | return null;
398 |
399 | return new Rgb(
400 | Convert.ToInt32(data["r"]),
401 | Convert.ToInt32(data["g"]),
402 | Convert.ToInt32(data["b"])
403 | );
404 | }
405 | set
406 | {
407 | // if value is set to null, reset the data
408 | if (value == null)
409 | {
410 | SetData("lightColor", null);
411 | return;
412 | }
413 |
414 | // No data changed
415 | if (LightColor.Red == value.Red && LightColor.Green == value.Green && LightColor.Blue == value.Blue)
416 | return;
417 |
418 | Dictionary dict = new Dictionary
419 | {
420 | { "r", value.Red },
421 | { "g", value.Green },
422 | { "b", value.Blue }
423 | };
424 | SetData("lightColor", dict);
425 | }
426 | }
427 |
428 | public Vector3 PositionInitial { get; internal set; }
429 |
430 | public Prop(Vector3 position, int dimension, uint range, ulong entityType) : base(entityType, position, dimension, range)
431 | {
432 | }
433 |
434 | public void SetRotation(Vector3 rot)
435 | {
436 | Rotation = rot;
437 | }
438 |
439 | public void SetPosition(Vector3 pos)
440 | {
441 | Position = pos;
442 | }
443 |
444 | public void Delete()
445 | {
446 | Prop.PropList.Remove(this);
447 | AltEntitySync.RemoveEntity(this);
448 | }
449 |
450 | public void Destroy()
451 | {
452 | Prop.PropList.Remove(this);
453 | AltEntitySync.RemoveEntity(this);
454 | }
455 | }
456 |
457 | public static class PropStreamer
458 | {
459 | ///
460 | /// Create a new dynamic object.
461 | ///
462 | /// The object model name.
463 | /// The position to spawn the object at.
464 | /// The rotation to spawn the object at(degrees).
465 | /// The dimension to spawn the object in.
466 | /// (Optional): Set object dynamic or not.
467 | /// (Optional): Set object frozen.
468 | /// (Optional): Set LOD distance.
469 | /// (Optional): set light color.
470 | /// (Optional): set object on fire(DOESN'T WORK PROPERLY YET!)
471 | /// (Optional): Set object texture variation.
472 | /// (Optional): Set object visibility.
473 | /// (Optional): The range that a player has to be in before the object spawns, default value is 400.
474 | /// The newly created dynamic object.
475 | public static Prop Create(
476 | uint model, Vector3 position, Vector3 rotation, int dimension = 0, bool? isDynamic = null, bool? placeObjectOnGroundProperly = false, bool? frozen = null, uint? lodDistance = null,
477 | Rgb lightColor = null, bool? onFire = null, TextureVariation? textureVariation = null, bool? visible = null, uint streamRange = 520
478 | )
479 | {
480 | Prop obj = new Prop(position, dimension, streamRange, 2)
481 | {
482 | Rotation = rotation,
483 | Model = model,
484 | Dynamic = isDynamic ?? null,
485 | Freeze = frozen ?? null,
486 | LodDistance = lodDistance ?? null,
487 | LightColor = lightColor ?? null,
488 | OnFire = onFire ?? null,
489 | TextureVariation = textureVariation ?? null,
490 | Visible = visible ?? null,
491 | PositionInitial = position,
492 | };
493 | Prop.PropList.Add(obj);
494 | AltEntitySync.AddEntity(obj);
495 | return obj;
496 | }
497 |
498 | public static bool Delete(ulong dynamicObjectId)
499 | {
500 | Prop obj = GetProp(dynamicObjectId);
501 |
502 | if (obj == null)
503 | return false;
504 | Prop.PropList.Remove(obj);
505 | AltEntitySync.RemoveEntity(obj);
506 | return true;
507 | }
508 |
509 | public static void Delete(Prop obj)
510 | {
511 | Prop.PropList.Remove(obj);
512 | AltEntitySync.RemoveEntity(obj);
513 | }
514 |
515 | public static Prop GetProp(ulong dynamicObjectId)
516 | {
517 | if (!AltEntitySync.TryGetEntity(dynamicObjectId, 2, out IEntity entity))
518 | {
519 | Console.WriteLine($"[Prop-Stream] [GetProp] ERROR: Entity with ID { dynamicObjectId } couldn't be found.");
520 | return default;
521 | }
522 |
523 | if (!(entity is Prop))
524 | return default;
525 |
526 | return (Prop)entity;
527 | }
528 |
529 | ///
530 | /// Destroy all created dynamic objects.
531 | ///
532 | public static void DestroyAllDynamicObjects()
533 | {
534 | foreach (Prop obj in GetAllProp())
535 | {
536 | AltEntitySync.RemoveEntity(obj);
537 | }
538 | Prop.PropList.Clear();
539 | }
540 |
541 | ///
542 | /// Get all created dynamic objects.
543 | ///
544 | /// A list of dynamic objects.
545 | public static List GetAllProp()
546 | {
547 | List objects = new List();
548 |
549 | foreach (IEntity entity in Prop.PropList)
550 | {
551 | Prop obj = GetProp(entity.Id);
552 |
553 | if (obj != null)
554 | objects.Add(obj);
555 | }
556 |
557 | return objects;
558 | }
559 |
560 | ///
561 | /// Get the dynamic object that's closest to a specified position.
562 | ///
563 | /// The position from which to check.
564 | /// The closest dynamic object to the specified position, or null if none found.
565 | public static (Prop obj, float distance) GetClosestDynamicObject(Vector3 pos)
566 | {
567 | if (GetAllProp().Count == 0)
568 | return (null, 5000);
569 |
570 | Prop obj = null;
571 | float distance = 5000;
572 |
573 | foreach (Prop o in GetAllProp())
574 | {
575 | float dist = Vector3.Distance(o.Position, pos);
576 | if (dist < distance)
577 | {
578 | obj = o;
579 | distance = dist;
580 | }
581 | }
582 |
583 | return (obj, distance);
584 | }
585 | }
586 | }
--------------------------------------------------------------------------------
/ServerSide/MarkerManager.cs:
--------------------------------------------------------------------------------
1 | using AltV.Net.Data;
2 | using AltV.Net.EntitySync;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Numerics;
6 |
7 | namespace EntityStreamer
8 | {
9 | ///
10 | /// Marker types.
11 | ///
12 | public enum MarkerTypes
13 | {
14 | MarkerTypeUpsideDownCone = 0,
15 | MarkerTypeVerticalCylinder = 1,
16 | MarkerTypeThickChevronUp = 2,
17 | MarkerTypeThinChevronUp = 3,
18 | MarkerTypeCheckeredFlagRect = 4,
19 | MarkerTypeCheckeredFlagCircle = 5,
20 | MarkerTypeVerticleCircle = 6,
21 | MarkerTypePlaneModel = 7,
22 | MarkerTypeLostMCDark = 8,
23 | MarkerTypeLostMCLight = 9,
24 | MarkerTypeNumber0 = 10,
25 | MarkerTypeNumber1 = 11,
26 | MarkerTypeNumber2 = 12,
27 | MarkerTypeNumber3 = 13,
28 | MarkerTypeNumber4 = 14,
29 | MarkerTypeNumber5 = 15,
30 | MarkerTypeNumber6 = 16,
31 | MarkerTypeNumber7 = 17,
32 | MarkerTypeNumber8 = 18,
33 | MarkerTypeNumber9 = 19,
34 | MarkerTypeChevronUpx1 = 20,
35 | MarkerTypeChevronUpx2 = 21,
36 | MarkerTypeChevronUpx3 = 22,
37 | MarkerTypeHorizontalCircleFat = 23,
38 | MarkerTypeReplayIcon = 24,
39 | MarkerTypeHorizontalCircleSkinny = 25,
40 | MarkerTypeHorizontalCircleSkinny_Arrow = 26,
41 | MarkerTypeHorizontalSplitArrowCircle = 27,
42 | MarkerTypeDebugSphere = 28,
43 | MarkerTypeDallorSign = 29,
44 | MarkerTypeHorizontalBars = 30,
45 | MarkerTypeWolfHead = 31
46 | }
47 |
48 | ///
49 | /// DynamicMarker class that stores all data related to a single marker.
50 | ///
51 | public class Marker : Entity, IEntity
52 | {
53 | private ulong EntityType
54 | {
55 | get
56 | {
57 | if (!TryGetData("entityType", out ulong type))
58 | return 999;
59 | return type;
60 | }
61 | set
62 | {
63 | // No data changed
64 | if (EntityType == value)
65 | return;
66 |
67 | SetData("entityType", value);
68 | }
69 | }
70 |
71 | ///
72 | /// Set or get the current marker's rotation (in degrees).
73 | ///
74 | public Vector3 Rotation
75 | {
76 | get
77 | {
78 | if (!TryGetData("rotation", out Dictionary data))
79 | return default;
80 |
81 | return new Vector3()
82 | {
83 | X = Convert.ToSingle(data["x"]),
84 | Y = Convert.ToSingle(data["y"]),
85 | Z = Convert.ToSingle(data["z"]),
86 | };
87 | }
88 | set
89 | {
90 | // No data changed
91 | if (Rotation.X == value.X && Rotation.Y == value.Y && Rotation.Z == value.Z && value != new Vector3(0, 0, 0))
92 | return;
93 |
94 | Dictionary dict = new Dictionary()
95 | {
96 | ["x"] = value.X,
97 | ["y"] = value.Y,
98 | ["z"] = value.Z,
99 | };
100 | SetData("rotation", dict);
101 | }
102 | }
103 |
104 | ///
105 | /// Set a texture dictionary, pass null to remove.
106 | ///
107 | public string TextureDict
108 | {
109 | get
110 | {
111 | if (!TryGetData("textureDict", out string textureDict))
112 | return null;
113 |
114 | return textureDict;
115 | }
116 | set
117 | {
118 | if (value == null)
119 | {
120 | SetData("textureDict", null);
121 | return;
122 | }
123 |
124 | // No data changed
125 | if (TextureDict == value)
126 | return;
127 |
128 | SetData("textureDict", value);
129 | }
130 | }
131 |
132 | ///
133 | /// Texture name, only applicable if TextureDict is set. pass null to reset value.
134 | ///
135 | public string TextureName
136 | {
137 | get
138 | {
139 | if (!TryGetData("textureName", out string textureName))
140 | return null;
141 |
142 | return textureName;
143 | }
144 | set
145 | {
146 | if (value == null)
147 | {
148 | SetData("textureName", null);
149 | return;
150 | }
151 |
152 | // No data changed
153 | if (TextureName == value)
154 | return;
155 |
156 | SetData("textureName", value);
157 | }
158 | }
159 |
160 | ///
161 | /// Whether the marker should rotate on the Y axis(heading).
162 | ///
163 | public bool? Rotate
164 | {
165 | get
166 | {
167 | if (!TryGetData("rotate", out bool rotate))
168 | return false;
169 |
170 | return rotate;
171 | }
172 | set
173 | {
174 | // if value is set to null, reset the data
175 | if (value == null)
176 | {
177 | SetData("rotate", false);
178 | return;
179 | }
180 |
181 | // No data changed
182 | if (Rotate == value)
183 | return;
184 |
185 | SetData("rotate", value);
186 | }
187 | }
188 |
189 | ///
190 | /// Whether the marker should be drawn onto the entity when they enter it.
191 | ///
192 | public bool? DrawOnEnter
193 | {
194 | get
195 | {
196 | if (!TryGetData("drawOnEnter", out bool drawOnEnter))
197 | return false;
198 |
199 | return drawOnEnter;
200 | }
201 | set
202 | {
203 | // if value is set to null, reset the data
204 | if (value == null)
205 | {
206 | SetData("drawOnEnter", false);
207 | return;
208 | }
209 |
210 | // No data changed
211 | if (DrawOnEnter == value)
212 | return;
213 |
214 | SetData("drawOnEnter", value);
215 | }
216 | }
217 |
218 | ///
219 | /// Whether the marker should rotate on the Y axis towards the player's camera.
220 | ///
221 | public bool? FaceCamera
222 | {
223 | get
224 | {
225 | if (!TryGetData("faceCam", out bool faceCamera))
226 | return false;
227 |
228 | return faceCamera;
229 | }
230 | set
231 | {
232 | // if value is set to null, reset the data
233 | if (value == null)
234 | {
235 | SetData("faceCam", false);
236 | return;
237 | }
238 |
239 | // No data changed
240 | if (FaceCamera == value)
241 | return;
242 |
243 | SetData("faceCam", value);
244 | }
245 | }
246 |
247 | ///
248 | /// Whether the marker should bob up and down.
249 | ///
250 | public bool? BobUpDown
251 | {
252 | get
253 | {
254 | if (!TryGetData("bobUpDown", out bool bobUpDown))
255 | return false;
256 |
257 | return bobUpDown;
258 | }
259 | set
260 | {
261 | // if value is set to null, reset the data
262 | if (value == null)
263 | {
264 | SetData("bobUpDown", false);
265 | return;
266 | }
267 |
268 | // No data changed
269 | if (BobUpDown == value)
270 | return;
271 |
272 | SetData("bobUpDown", value);
273 | }
274 | }
275 |
276 | ///
277 | /// Set scale of the marker.
278 | ///
279 | public Vector3 Scale
280 | {
281 | get
282 | {
283 | if (!TryGetData("scale", out Dictionary data))
284 | return default;
285 |
286 | return new Vector3()
287 | {
288 | X = Convert.ToSingle(data["x"]),
289 | Y = Convert.ToSingle(data["y"]),
290 | Z = Convert.ToSingle(data["z"]),
291 | };
292 | }
293 | set
294 | {
295 | // No data changed
296 | if (Scale.X == value.X && Scale.Y == value.Y && Scale.Z == value.Z && value != new Vector3(0, 0, 0))
297 | return;
298 |
299 | Dictionary dict = new Dictionary()
300 | {
301 | ["x"] = value.X,
302 | ["y"] = value.Y,
303 | ["z"] = value.Z,
304 | };
305 | SetData("scale", dict);
306 | }
307 | }
308 |
309 | ///
310 | /// Represents a heading on each axis in which the marker should face, alternatively you can rotate each axis independently with Rotation and set Direction axis to 0.
311 | ///
312 | public Vector3 Direction
313 | {
314 | get
315 | {
316 | if (!TryGetData("direction", out Dictionary data))
317 | return default;
318 |
319 | return new Vector3()
320 | {
321 | X = Convert.ToSingle(data["x"]),
322 | Y = Convert.ToSingle(data["y"]),
323 | Z = Convert.ToSingle(data["z"]),
324 | };
325 | }
326 | set
327 | {
328 | // No data changed
329 | if (Direction != null && Direction.X == value.X && Direction.Y == value.Y && Direction.Z == value.Z && value != new Vector3(0, 0, 0))
330 | return;
331 |
332 | Dictionary dict = new Dictionary()
333 | {
334 | ["x"] = value.X,
335 | ["y"] = value.Y,
336 | ["z"] = value.Z,
337 | };
338 | SetData("direction", dict);
339 | }
340 | }
341 |
342 | ///
343 | /// Set or get the current marker's type(see MarkerTypes enum).
344 | ///
345 | public MarkerTypes MarkerType
346 | {
347 | get
348 | {
349 | if (!TryGetData("markerType", out int markerType))
350 | return default;
351 |
352 | return (MarkerTypes)markerType;
353 | }
354 | set
355 | {
356 | // No data changed
357 | if (MarkerType == value)
358 | return;
359 |
360 | SetData("markerType", (int)value);
361 | }
362 | }
363 |
364 | ///
365 | /// Set marker color.
366 | ///
367 | public Rgba? Color
368 | {
369 | get
370 | {
371 | if (!TryGetData("color", out Dictionary data))
372 | return null;
373 |
374 | return new Rgba(
375 | Convert.ToByte(data["r"]),
376 | Convert.ToByte(data["g"]),
377 | Convert.ToByte(data["b"]),
378 | Convert.ToByte(data["a"])
379 | );
380 | }
381 | set
382 | {
383 | // if value is set to null, reset the data
384 | if (value == null)
385 | {
386 | SetData("color", null);
387 | return;
388 | }
389 |
390 | // No data changed
391 | if (Color != null && Color?.R == value?.R && Color?.G == value?.G && Color?.B == value?.B && Color?.A == value?.A)
392 | return;
393 |
394 | Dictionary dict = new Dictionary
395 | {
396 | { "r", Convert.ToInt32( value?.R ) },
397 | { "g", Convert.ToInt32( value?.G ) },
398 | { "b", Convert.ToInt32( value?.B ) },
399 | { "a", Convert.ToInt32( value?.A ) }
400 | };
401 |
402 | SetData("color", dict);
403 | }
404 | }
405 |
406 | private static List markerList = new List();
407 |
408 | public static List MarkerList
409 | {
410 | get
411 | {
412 | lock (markerList)
413 | {
414 | return markerList;
415 | }
416 | }
417 | set
418 | {
419 | markerList = value;
420 | }
421 | }
422 |
423 | public Marker(Vector3 position, int dimension, uint range, ulong entityType) : base(entityType, position, dimension, range)
424 | {
425 | EntityType = entityType;
426 | }
427 |
428 | ///
429 | /// Destroy this marker.
430 | ///
431 | public void Destroy()
432 | {
433 | Marker.MarkerList.Remove(this);
434 | AltEntitySync.RemoveEntity(this);
435 | }
436 | }
437 |
438 | public static class MarkerStreamer
439 | {
440 | ///
441 | /// Create a new dynamic marker
442 | ///
443 | /// The type of marker to spawn.
444 | /// The position at which the marker should spawn at.
445 | /// The scale of the marker.
446 | /// The rotation of the marker.
447 | /// The direction of the marker.
448 | /// The color of the marker.
449 | /// Whether the marker should bob up and down.
450 | /// Whether the marker should face the entity's camera.
451 | /// Whether the marker should rotate on the Y axis only.
452 | /// An optional texture dictionary to apply to the marker.
453 | /// An optional texture name to apply to the marker.
454 | /// Whether it should draw the marker onto an entity that intersects with it.
455 | /// The dimension of the marker
456 | /// Stream distance of the marker, default is 100.
457 | ///
458 | public static Marker Create(
459 | MarkerTypes markerType, Vector3 position, Vector3 scale, Vector3? rotation = null, Vector3? direction = null, Rgba? color = null, int dimension = 0,
460 | bool? bobUpDown = false, bool? faceCamera = false, bool? rotate = false, string textureDict = null, string textureName = null,
461 | bool? drawOnEnter = false, uint streamRange = 100
462 | )
463 | {
464 | Marker marker = new Marker(position, dimension, streamRange, 0)
465 | {
466 | Rotation = rotation ?? new Vector3(0),
467 | MarkerType = markerType,
468 | Direction = direction ?? new Vector3(0),
469 | Scale = scale,
470 | Color = color ?? null,
471 | BobUpDown = bobUpDown ?? null,
472 | FaceCamera = faceCamera ?? null,
473 | Rotate = rotate ?? null,
474 | TextureDict = textureDict ?? null,
475 | TextureName = textureName ?? null,
476 | DrawOnEnter = drawOnEnter ?? null
477 | };
478 | Marker.MarkerList.Add(marker);
479 | AltEntitySync.AddEntity(marker);
480 | return marker;
481 | }
482 |
483 | ///
484 | /// Destroy a dynamic marker by it's ID.
485 | ///
486 | /// The ID of the marker.
487 | /// True if successful, false otherwise.
488 | public static bool Delete(ulong dynamicMarkerId)
489 | {
490 | Marker marker = GetMarker(dynamicMarkerId);
491 |
492 | if (marker == null)
493 | return false;
494 |
495 | Marker.MarkerList.Remove(marker);
496 | AltEntitySync.RemoveEntity(marker);
497 | return true;
498 | }
499 |
500 | ///
501 | /// Destroy a dynamic marker.
502 | ///
503 | /// The marker instance to destroy.
504 | public static void Delete(Marker marker)
505 | {
506 | Marker.MarkerList.Remove(marker);
507 | AltEntitySync.RemoveEntity(marker);
508 | }
509 |
510 | ///
511 | /// Get a dynamic marker by it's ID.
512 | ///
513 | /// The ID of the marker.
514 | /// The dynamic marker or null if not found.
515 | public static Marker GetMarker(ulong MarkerId)
516 | {
517 | if (!AltEntitySync.TryGetEntity(MarkerId, 0, out IEntity entity))
518 | {
519 | Console.WriteLine($"[MARKER-STREAMER] [GetDynamicMarker] ERROR: Entity with ID { MarkerId } couldn't be found.");
520 | return null;
521 | }
522 |
523 | if (!(entity is Marker))
524 | return null;
525 |
526 | return (Marker)entity;
527 | }
528 |
529 | ///
530 | /// Destroy all created dynamic markers.
531 | ///
532 | public static void DestroyAllDynamicMarkers()
533 | {
534 | foreach (Marker marker in GetAllDynamicMarkers())
535 | {
536 | AltEntitySync.RemoveEntity(marker);
537 | }
538 | Marker.MarkerList.Clear();
539 | }
540 |
541 | ///
542 | /// Get all created dynamic markers.
543 | ///
544 | /// A list of dynamic markers.
545 | public static List GetAllDynamicMarkers()
546 | {
547 | List markers = new List();
548 |
549 | foreach (IEntity entity in Marker.MarkerList)
550 | {
551 | Marker obj = GetMarker(entity.Id);
552 |
553 | if (obj != null)
554 | markers.Add(obj);
555 | }
556 |
557 | return markers;
558 | }
559 | }
560 | }
--------------------------------------------------------------------------------