Skip to content

Commit

Permalink
Update to Streamer Plugin v2.9.4
Browse files Browse the repository at this point in the history
Fixes #571
  • Loading branch information
RussellLVP committed May 16, 2020
1 parent b5b062e commit 05eaccf
Show file tree
Hide file tree
Showing 9 changed files with 355 additions and 369 deletions.
12 changes: 12 additions & 0 deletions NOTES.md
@@ -0,0 +1,12 @@
# Notes
This document contains a series of notes and commands we've collected over the years for making
building all of our plugins and dependencies as easy as possible.

## Compiling the various plugins on Linux
### samp-streamer-plugin
$ git clone https://github.com/samp-incognito/samp-streamer-plugin.git
$ cd samp-streamer-plugin
$ git submodule update --init --recursive . ":(exclude)lib/boost"
$ mkdir build && cd build
$ cmake .. -DBOOST_ROOT=/opt/boost_1_73_0_32bit -DCMAKE_BUILD_TYPE=Release
$ make streamer_unity
173 changes: 67 additions & 106 deletions javascript/entities/game_object.js
Expand Up @@ -4,146 +4,107 @@

// Defines the interface available to game objects created for players in the game. New objects
// are only meant to be constructed through the ObjectManager.
class GameObject {
constructor(manager, options) {
this.manager_ = manager;
export class GameObject {
// Id to represent an invalid object. Maps to INVALID_STREAMER_ID, which is (0).
static kInvalidId = 0;

this.modelId_ = options.modelId;
#id_ = null;
#manager_ = null;

this.drawDistance_ = options.drawDistance;
this.streamDistance_ = options.streamDistance;
#modelId_ = null;
#drawDistance_ = null;
#streamDistance_ = null;

this.virtualWorld_ = options.virtualWorld;
this.interiorId_ = options.interiorId;
#interiors_ = null;
#virtualWorlds_ = null;

this.id_ = pawnInvoke('CreateDynamicObjectEx', 'iffffffffaaaaiiiii', options.modelId,
options.position.x, options.position.y, options.position.z,
options.rotation.x, options.rotation.y, options.rotation.z,
options.streamDistance, options.drawDistance, [options.virtualWorld],
[options.interiorId], [-1], [-1], 0 /* priority */, 1 /* maxworlds */,
1 /* maxinteriors */, 1 /* maxplayers */, 1 /* maxareas */);

if (this.id_ === GameObject.INVALID_ID)
throw new Error('Unable to create the object with model Id #' + options.modelId);

this.cameraCollisionsEnabled_ = true;
this.moving_ = false;

// Promise resolver that is to be used when the current movement has to be prematurely
// finished, for example because another move started or because it was aborted altogether.
this.stopCurrentMoveResolver_ = null;
constructor(manager) {
this.#manager_ = manager;
}

// Gets the id the streamer assigned to this object.
get id() { return this.id_; }
initialize(options) {
this.#modelId_ = options.modelId;

// Returns whether the object still exists on the server.
isConnected() { return this.id_ !== null; }
this.#drawDistance_ = options.drawDistance;
this.#streamDistance_ = options.streamDistance;

// Gets the Id of the model that is visually epresenting this object.
get modelId() { return this.modelId_; }
this.#interiors_ = options.interiors;
this.#virtualWorlds_ = options.virtualWorlds;

// Gets or sets the position of the object in the world.
get position() { return new Vector(...pawnInvoke('GetDynamicObjectPos', 'iFFF', this.id_)); }
set position(value) {
pawnInvoke('SetDynamicObjectPos', 'ifff', this.id_, value.x, value.y, value.z);
this.#id_ = this.createInternal(options);
if (this.#id_ === GameObject.kInvalidId)
throw new Error('Unable to create the object with model Id #' + options.modelId);
}

// Gets or sets the rotation, in 3D space, of the object in the world.
get rotation() { return new Vector(...pawnInvoke('GetDynamicObjectRot', 'iFFF', this.id_)); }
set rotation(value) {
pawnInvoke('SetDynamicObjectRot', 'ifff', this.id_, value.x, value.y, value.z);
// Creates the actual object on the server. May be overridden for testing purposes.
createInternal(options) {
return pawnInvoke('CreateDynamicObjectEx', 'iffffffffaaaaiiiii',
/* modelid= */ options.modelId,
/* x= */ options.position.x,
/* y= */ options.position.y,
/* z= */ options.position.z,
/* rx= */ options.rotation.x,
/* ry= */ options.rotation.y,
/* rz= */ options.rotation.z,
/* streamdistance= */ options.streamDistance,
/* drawDistance= */ options.drawDistance,
/* worlds= */ options.virtualWorlds,
/* interiors= */ options.interiors,
/* players= */ options.players,
/* areas= */ options.areas,
/* priority= */ options.priority,
/* maxworlds= */ options.virtualWorlds.length,
/* maxinteriors= */ options.interiors.length,
/* maxplayers= */ options.players.length,
/* maxareas= */ options.areas.length);
}

// Gets the draw distance of the object.
get drawDistance() { return this.drawDistance_; }

// Gets the streaming distance the streamer will apply to this object.
get streamDistance() { return this.streamDistance_; }

// Gets the virtual world in which this object will be visible. Will return -1 when the object
// should be visible in all virtual worlds.
get virtualWorld() { return this.virtualWorld_; }
// Destroys the actual object on the server. May be overridden for testing purposes.
destroyInternal() { pawnInvoke('DestroyDynamicObject', 'i', this.#id_); }

// Gets the interior Id in which the object will be visible. Will return -1 when the object
// should be visible in all interiors.
get interiorId() { return this.interiorId_; }

// Returns whether camera collisions are enabled for this object. They are by default.
areCameraCollisionsEnabled() {
return !!pawnInvoke('GetDynamicObjectNoCameraCol', 'i', this.id_);
}
// ---------------------------------------------------------------------------------------------

// Disables collisions between the player's camera and the object. This only has an effect
// outside of the common San Andreas map coordinates (beyond -3000 and 3000).
disableCameraCollisions() { pawnInvoke('SetDynamicObjectNoCameraCol', 'i', this.id_); }
get id() { return this.#id_; }

// Returns whether the object is currently moving.
isMoving() { return !!pawnInvoke('IsDynamicObjectMoving', 'i', this.id_); }
get modelId() { return this.#modelId_; }
get drawDistance() { return this.#drawDistance_; }
get streamDistance() { return this.#streamDistance_; }

// Moves the object to |position| with |rotation| over a period of |durationMs| milliseconds.
// This an asynchronous function, and can be waited on to finish.
async moveTo(position, rotation, durationMs) {
if (this.stopCurrentMoveResolver_)
this.stopCurrentMoveResolver_();
get interiors() { return this.#interiors_; }
get virtualWorlds() { return this.#virtualWorlds_; }

const distanceUnits = position.distanceTo(this.position);
const speed = distanceUnits / (durationMs / 1000);
isConnected() { return this.#id_ !== GameObject.kInvalidId; }

pawnInvoke('MoveDynamicObject', 'ifffffff', this.id_, position.x, position.y, position.z,
speed, rotation.x, rotation.y, rotation.z);
// ---------------------------------------------------------------------------------------------

const stopPromise = new Promise(resolve => this.stopCurrentMoveResolver_ = resolve);
await Promise.race([
wait(durationMs),
stopPromise
]);

this.stopCurrentMoveResolver_ = null;
}

// Stops moving the object immediately.
stopMove() {
pawnInvoke('StopDynamicObject', 'i', this.id_);
if (this.stopCurrentMoveResolver_)
this.stopCurrentMoveResolver_();
get position() { return new Vector(...pawnInvoke('GetDynamicObjectPos', 'iFFF', this.#id_)); }
set position(value) {
pawnInvoke('SetDynamicObjectPos', 'ifff', this.#id_, value.x, value.y, value.z);
}

// Attaches the object to |object| at |offset| with |rotation|, both of which must be vectors.
// Optionally |synchronize| can be set, which will synchronize the |object| when this one moves.
attachToObject(object, offset, rotation, synchronize = false) {
pawnInvoke('AttachDynamicObjectToObject', 'iiffffffi', this.id_, object.id, offset.x,
offset.y, offset.z, rotation.x, rotation.y, rotation.z, synchronize ? 1 : 0);
get rotation() { return new Vector(...pawnInvoke('GetDynamicObjectRot', 'iFFF', this.#id_)); }
set rotation(value) {
pawnInvoke('SetDynamicObjectRot', 'ifff', this.#id_, value.x, value.y, value.z);
}

// Attaches the object to |player| at |offset| with |rotation|, both of which must be vectors.
attachToPlayer(player, offset, rotation) {
pawnInvoke('AttachDynamicObjectToPlayer', 'iiffffff', this.id_, player.id, offset.x,
offset.y, offset.z, rotation.x, rotation.y, rotation.z);
}
// ---------------------------------------------------------------------------------------------

// Attaches the object to |vehicle| at |offset| with |rotation|, both of which must be vectors.
attachToVehicle(vehicle, offset, rotation) {
pawnInvoke('AttachDynamicObjectToVehicle', 'iiffffff', this.id_, vehicle.id, offset.x,
pawnInvoke('AttachDynamicObjectToVehicle', 'iiffffff', this.#id_, vehicle.id, offset.x,
offset.y, offset.z, rotation.x, rotation.y, rotation.z);
}

// Enables the object editor for this object for |player|.
enableEditorForPlayer(player) { pawnInvoke('EditDynamicObject', 'ii', player.id, this.id_); }

// TODO(Russell): Design an API for the material-related natives available to objects.
// ---------------------------------------------------------------------------------------------

dispose() {
pawnInvoke('DestroyDynamicObject', 'i', this.id_);
this.id_ = null;
this.destroy();

this.manager_.didDisposeObject(this);
this.manager_ = null;
this.#id_ = GameObject.kInvalidId;

this.#manager_.didDisposeObject(this);
this.#manager_ = null;
}
}

// The Id that is used to represent invalid objects (INVALID_STREAMER_ID in Pawn).
GameObject.INVALID_ID = 0;

// Expose the GameObject object globally since it will be commonly used.
global.GameObject = GameObject;
15 changes: 11 additions & 4 deletions javascript/entities/object_manager.js
Expand Up @@ -16,17 +16,24 @@ class ObjectManager {
// Creates a new object with the given options. The options are based on the available settings
// as part of the Object Streamer, and can be changed after the object's creation.
createObject({ modelId, position, rotation, interiorId = -1, virtualWorld = -1 } = {}) {
const object = new this.objectConstructor_(this, {
const object = new this.objectConstructor_(this);

// Initializes the |object| with all the configuration passed to the manager.
object.initialize({
modelId: modelId,

position: position,
rotation: rotation,

interiorId: interiorId,
virtualWorld: virtualWorld,
interiors: [ interiorId ],
virtualWorlds: [ virtualWorld ],

streamDistance: 300,
drawDistance: 0
drawDistance: 0,
priority: 0,

players: [ -1 ],
areas: [ -1 ],
});

this.objects_.add(object);
Expand Down
12 changes: 6 additions & 6 deletions javascript/entities/object_manager.test.js
Expand Up @@ -2,13 +2,13 @@
// Use of this source code is governed by the MIT license, a copy of which can
// be found in the LICENSE file.

import { MockGameObject } from 'entities/test/mock_game_object.js';
import ObjectManager from 'entities/object_manager.js';
import MockObject from 'entities/test/mock_object.js';

describe('ObjectManager', (it, beforeEach, afterEach) => {
let manager = null;

beforeEach(() => manager = new ObjectManager(MockObject /* objectConstructor */));
beforeEach(() => manager = new ObjectManager(MockGameObject /* objectConstructor */));
afterEach(() => {
if (manager)
manager.dispose();
Expand Down Expand Up @@ -67,13 +67,13 @@ describe('ObjectManager', (it, beforeEach, afterEach) => {
assert.deepEqual(object.rotation, new Vector(2, 2, 2));
assert.equal(object.drawDistance, 0);
assert.equal(object.streamDistance, 300);
assert.equal(object.virtualWorld, 42);
assert.equal(object.interiorId, 7);
assert.equal(object.virtualWorlds[0], 42);
assert.equal(object.interiors[0], 7);

const defaultObject = manager.createObject({ modelId: 1225, position: new Vector(0, 0, 0),
rotation: new Vector(0, 0, 0) });

assert.equal(defaultObject.virtualWorld, -1);
assert.equal(defaultObject.interiorId, -1);
assert.equal(defaultObject.virtualWorlds[0], -1);
assert.equal(defaultObject.interiors[0], -1);
});
});
8 changes: 4 additions & 4 deletions javascript/entities/scoped_entities.test.js
Expand Up @@ -200,8 +200,8 @@ describe('ScopedEntities', it => {

const object = entities.createObject({ modelId: 1225, position: new Vector(1, 2, 3),
rotation: new Vector(4, 5, 6) });
assert.equal(object.interiorId, -1);
assert.equal(object.virtualWorld, -1);
assert.deepEqual(object.interiors, [-1]);
assert.deepEqual(object.virtualWorlds, [-1]);

const pickup = entities.createPickup({ modelId: 322, position: new Vector(0, 0, 0) });
assert.equal(pickup.virtualWorld, 0);
Expand All @@ -225,8 +225,8 @@ describe('ScopedEntities', it => {

const object = entities.createObject({ modelId: 1225, position: new Vector(1, 2, 3),
rotation: new Vector(4, 5, 6) });
assert.equal(object.interiorId, 7);
assert.equal(object.virtualWorld, 42);
assert.deepEqual(object.interiors, [7]);
assert.deepEqual(object.virtualWorlds, [42]);

const pickup = entities.createPickup({ modelId: 322, position: new Vector(0, 0, 0) });
assert.equal(pickup.virtualWorld, 42);
Expand Down
38 changes: 38 additions & 0 deletions javascript/entities/test/mock_game_object.js
@@ -0,0 +1,38 @@
// Copyright 2020 Las Venturas Playground. All rights reserved.
// Use of this source code is governed by the MIT license, a copy of which can
// be found in the LICENSE file.

import { GameObject } from 'entities/game_object.js';

// Global counter for creating a unique mocked object ID.
let globalMockObjectId = 0;

// Mocked version of the GameObject, which represents objects created in the world of San Andreas.
// Overrides all functionality that would end up calling into Pawn and/or the streamer plugin.
export class MockGameObject extends GameObject {
#position_ = null;
#rotation_ = null;

// Overridden to avoid creating a real object on the server.
createInternal(options) {
this.#position_ = options.position;
this.#rotation_ = options.rotation;

return ++globalMockObjectId;
}

// Overridden to avoid destroying a real object on the server.
destroy() {}

// ---------------------------------------------------------------------------------------------

get position() { return this.#position_; }
set position(value) { this.#position_ = value; }

get rotation() { return this.#rotation_; }
set rotation(value) { this.#rotation_ = value; }

// ---------------------------------------------------------------------------------------------

attachToVehicle(vehicle, offset, rotation) {}
}

0 comments on commit 05eaccf

Please sign in to comment.