Jump to content

[REL] Project trains 2.0.0


IIYAMA

Recommended Posts

 

Project trains

This resource creates multiple trains driving around the map and make them drive automatic. It makes San Andreas a little bit more alive. You can set-up how much trains are created, how fast they go, minimal/maximal amount of trainscarts they are pulling and some more.

 

 

 

Versions

Version 1.0.0

  • Serverside time synced trains for multiplayer
  • Admin panel settings

 

Version 2.0.0

  • Uncompiled
  • Better optimised than previous versions.
    CPU (3,5-3,9 GHz)
    • 4 trains +/- 1.22% usage
    • 40 trains  +/- 7.05% usage Overkill
  • Cleaned up a lot of the old code (I wrote this in 2015.. , never the less it is still ugly :C)
  • Added some custom train-cart designs.
  • Fixed all known bugs.
  • Trains won't stop, even in this version. But you can try to make that yourself...
  • Improved bandwidth
  • You can design your own traincarts in the map editor, see documentation after clicking on the lazy download link

 

Bugs:

GTA spawn bug: Incorrect train spawn, see documentation.

 

Repository

 

 

mta-screen-2020-04-03-22-36-31.png

Lazy <you> transport

 

mta-screen-2020-04-03-22-38-54.png

Armed to the teeth

 

mta-screen-2020-04-09-22-33-31.png

Admin panel settings

 

Building your own traincarts with the map editor

Spoiler

mta-screen-2020-04-09-22-24-28.png

Editor template (/traincart-design-template/)

 

 

mta-screen-2020-04-09-22-25-34.png

New traincart

 

 

Download here - project trains 2.0.0

 

Edited by IIYAMA
change name
  • Like 8
Link to post

@Markeloff

Yea, that's because of that railroad track problem. I can't wait for those getTrainTrack() and setTrainTrack() features in 1.6, so I can fix it. But I have no idea how long it will last until the release.

You don't need to create trains when the player is idle.

The trains are never been destroyed, they travel around the map like remote players do.

But I chance this when those features in 1.6 come out, for now I keep it like this because when a train is created, it picks it's track. Once that happens, it can never be another track. So the script has to recreate trains until they pick the right one.

Which will be checked by comparing the distance between the rail-road and the train itself.

Thank you for your feedback!

Link to post

Btw. The beta resource is only available here. (not on the resource page)

When the time comes, I will add full release on both.

In my opinion beta resources are not meant for the resource page.

Also if you find any more bugs next to misplaced trains or missing traincarts.

Let me know, the more feedback I get the more I can fix. :)

Link to post

Well you can't deny where you got the inspiration at least (GTWtrain) ;), although it looks pretty nice so far and I've been waiting for those functions as well, not just for a train system but they are actually useful for a lot of stuff like making switch points and stuff like that. After trying it I have some questions tho:

  • I noticed that the client file is pretty big while the server file are smaller, are the train client sided?
  • Did you disable the trains to stream out in order to keep them on the tracks or are you using something else?
  • Does the trains spawn near players or are they always existing?

It's nice with some competition in this area as MTA certainly need some kind of train system. That deals with all the issues you are mentioning. Although I got to object about the performance, running both the systems parallell while both systems managed 1 streamed in train each this was the results:

wZljqEH.png

Note that you're using between 25-30% CPU client side (intel core i7 950 @3GHz), while GTWtrain uses around 0.5% and 0% when there's no train nearby (server side (intel pentium @1.2GHz)). Are you using "onClientRender" to update the train speed?

Edited by Guest
Link to post

I took a look at it. But my inspiration came from gta itself. GTWtrain also took it's inspiration from GTA san, so...

Or well I can better say you did...

1 Uses a lot of client memory and cpu.

About the usage. As I said on top, client side isn't optimised. (far from it)

- Trains are never be destroyed and they are clientside.

- Yes, onClientPreRender. In order to keep it perfect synced without lagg. And no, I am not changing the speed. If I do this by changing the speed, the result would be a desync. You might say that rendering a position uses too much memory, I disagree with that. Your info about performance is based on 15* trains, which will be in the full release the streamed in trains(1).

- I didn't disable the streamed in/out functionality and it doesn't have to be. When the trains are created they can't change track.

*Depends how many you enable.

AND a both all, this IS A BETA. What matters is that it works, optimisation comes with the full release.

Go record these performance statics with the full release. Using them on a beta resource vs full resource is just...

You might see this as a competition, but I am not busy with that part. I just want to build a perfect project which I like and doing it my way. If you want a competition, then I will decline it. /me not interested in that kind of childish stuff.

Link to post

You probably misunderstood my point, I'm not complaining nor trying to start some kind of fight. I'm just comparing the systems, maybe you know something I don't know or vice versa. Knowledge that could improve both these systems.

I also see how "onClientRender" could create a perfect sync in theory but what if two different players has different FPS? It might be better than timers, but after all I don't really think that the sync can be done better than what the server are able to perform by the regular sync of server side objects. After all there's still latency and other network related delays to consider.

While taking advantage of both server and client side, have you though of the idea using peds as engineers and use setControlState to control the trains? That and a list of points with a predefined "goal" speed might be the solution to allow the trains to stop at stations, or to drive at different speed in general or what do you think?

Link to post

Yea, about that competition I probably misunderstood. Since you mentioned it, it was bugging me.

About the onClientRender > onClientPreRender part. (onClientRender is too slow) If players have different fps, they both see the same train-speed. Yet, the one with the highest fps will see a smoother animation. When players their fps are not the same, the collision detection is not the same. Not only with onClientPreRender but the same with setControlState, because this is something from gta. To prevent misunderstandings, I am not setting any trainspeed but it's position.

Why position instead of trainspeed?

You can predict it's next position(per frame) when you know how long the railroad is + how fast the custom trainspeed is.

That is the big secret behind it... getTickCount() :mrgreen:

Which gives me the problem with stopping the train at stations.

Why not using peds?

There are too many players with bad internet, circa 70% of players that visit my stealth server. I just want stable trains controlled by serverside and executed by clientside. That's main reason why I build it this way.

Anyway your script can also use some optimisation. You don't need a sync timer for all trains.

Serverside can only execute every(all timers from the whole server) timer once in the circa 65 ms. Within those circa 65 ms no timer can get executed. You might think those timers are accurate, but they are far from it.

1 timer executing a loop would be enough.

You can test it yourself with:(serverside)

local lastExecution = 0 
  
local executionFunction function (timerName) 
local timeNow = getTickCount() 
outputDebugString(timerName .. " " .. timeNow-lastExecution .. " delay (ms)") 
lastExecution = timeNow 
end 
  
addEventHandler("onResourceStart",resourceRoot, 
function () 
  
local startNextTimer = function  () -- prepare the code of the second timer, so it has no delay when creating. 
setTimer(executionFunction,50,0,"timer2") 
end 
  
lastExecution = getTickCount() 
  
setTimer(executionFunction,50,0,"timer1") 
setTimer(startNextTimer,75,0) -- add an extra delay of 25 ms. 
  
end) 

Link to post

The normal way to sync positions should be ever controlled serverside (NEVER trust a client!).

All position calculations should be done by server (timer speed is enough for this) and interpolated by each client between two server ticks. So it looks smooth and is syncronized cause all clients got same positions on each tick.

BTW: Good job and dont stop to try out different technics.

Link to post

That sounds very interesting, I did notice how perfect the train "streamed in", passed then disappeared from my sight far away. That part surely works better than my version, seeing it live with multiple players around would be even more interesting. But why setting position instead of train speed? doesn't that mean that you have to calculate a lot to find out direction, next node on the tracks and so on? As far as I know all trains follow nodes on various distance placed along the tracks and when a new train are spawned it spawns at the nearest node, maybe it spawns between nodes as well.

When it comes to peds, I was inspired by the bot system by Crystal which controls peds and cars. As it seems to be using peds to control the cars they move just as smooth as if a player would have driven it. The main problem here tho is the element syncer as all moving objects in MTA has a so called "syncelement" which is a player, if this is a player with really high ping then all the cars start to lag and sync terrible as well.

There's a function to change the element syncer tho but I don't know if it always overrides or if its ignored as soon another player get's closer to the object or something like that. An improvement for the sync may be to force the nearest player with lowest ping at the moment be the sync element for the train. That kind of stuff could probably be set in onClientPreRender if it doesn't use to much CPU.

I believe you about the timers, those where implemented from the beginning and is now the core of my system so solving that would require a complete rewrite of the system. I'm still using timers for some stuff but those should be running with a few seconds delay and not be used for any of the sync stuff. Although, clients usually has better CPU's and they only have to calculate their own data so building the sync stuff client side is probably the best solution so far. In any case I'm really looking forward to see the final version live. Good luck.

Link to post

Yea, I have to calculate a lot. But that isn't really a problem, because I know how to do it.

About spawning trains at the nearest spawnpoint, I don't have to spawn them at a spawnpoint.

When you take the railroad, which is ... long, you splits it up in the amount of trains. You calculate the progress and you keep moving them on the correct location. Full version: Al thought there are no elements when the trains are streamed out. You still have to calculate where they are in order to keep it perfect synced.

About timers, did you ever used the resource missiontimer?

Well the thing about timers is that MTA is handling those. But somehow if I use too much of them they are starting to lagg.

So if you have too much timers, you can also choose for lua to handle them. And in my opinion it works pretty well. Edit it as you want. (I just wrote it, it is NOT checked if everything is correct)

local globalTimer = false 
local globalTimerContainer = {} 
local globalTimerFunction = function () 
    local timeNow = getTickCount() 
    for i=#globalTimerContainer,1,-1 do -- inverse the loop, important for table.remove. 
        local timerData = globalTimerContainer[i] 
        if timeNow > timerData["endTime"] then 
            timerData["callbackFunction"](unpack(timerData["arguments"])) 
            table.remove(globalTimerContainer,i) 
        end 
    end 
    if #globalTimerContainer == 0 then 
        killTimer(globalTimer) 
        globalTimer = false 
    end 
end 
  
local addTimer = function (...) 
    local timerData = {...} 
    local endTime = timerData[1] 
    table.remove(timerData,1) 
    local callbackFunction = timerData[1] 
    table.remove(timerData,1) 
    globalTimerContainer[#globalTimerContainer+1] = {["endTime"]=endTime,["callbackFunction"]=callbackFunction,["arguments"]=timerData} 
    if not globalTimer or not isTimer(globalTimer) then 
        globalTimer = setTimer(globalTimerFunction,100,0) 
    end 
end 
--addTimer(endTime,callbackFunction,argument1,argument2,argument3, etc.) 
  
  
-------------------------------- 
function callThisFunctionBack(argument) 
    outputChatBox(argument) 
end 
  
--addTimer(endTime,callbackFunction,argument1,argument2,argument3, etc.) 
addTimer(getTickCount()+1300,callThisFunctionBack,"IIYAMA") 
  
  

Link to post

Well there is an alternative way do make persistent, synced non player trains, and since it doesn't use the default MTA train system, it can work with custom train tracks as well:

https://wiki.multitheftauto.com/wiki/MoveObject

Yes I know - “You can't move vehicles using moveObject directly”

However (in case of trains) You can derail a train, attach it to an transparent object without collisions and move that object. Doing it server side makes for almost perfect sync.

This is a very old function that I've used in some projects:

  
function bot:serverAI() 
    if isElement(self.obj) then 
        warpPedIntoVehicle(self.ped,self.veh) 
        local x,y,z = getElementPosition(self.obj) 
        local rx,ry,rz = getElementRotation(self.obj) 
        local tx,ty,tz = getElementPosition(self.way) 
        local waypointangle = ( 360 - math.deg ( math.atan2 ( ( tx - x ), ( ty - y ) ) ) ) % 360 
        local speedlimit = getElemetData(self.way,"speedlimit") 
        local hangle = ( 360 - math.deg ( math.atan2 ( ( tx - x ), ( tz - z ) ) ) ) % 360 
        setElementRotation (self.obj,hangle,ry,waypointangle) 
        local distance = getdistanceBetweenPoints3D(x,y,z,tx,ty,tz) 
        local time = (10*distance)/(36*speedlimit) 
        moveobject(self.obj,time,tx,ty,tz+1) 
        setTimer(function(tab) 
            if isElement(tab.obj) then 
                local tempwayid = getElemetData(tab.way,"next_waypoint") or "" 
                local tempway = getElementById(tempwayid) 
                if not iselement(tempway) then 
                    tempwayid = getElemetData(tab.way,"master_waypoint") or "" 
                    tempway = getElementById(tempwayid) 
                end 
                tab.way = tempway 
                tab:serverAI() 
                 
            end 
        end,time+50,1,self) 
    end 
end 
  

used in this video for handling vehicles without syncers:

If you look at the most distant vehicles you may notice the transition between handling by server and handling by client. To use with train I think it should be handled 100% server side.

The one thing that needs to be done is a path recording tool to create the waypoints (nodes).

This uses one timer and moveObject function per vehicle at ay given time (may get problematic with thunders of vehicles but 15 trains x 6 wagons should be stable), the calculations are similar but executed per node, not per frame, and the sync is handled 100% by MTA.The acceleration can be simulated using strEasingType argument in moveObjest.

Link to post

Possible, yet you will use more bandwidth and server power.Two things you don't want.

And the most critical part of moveObject is that it isn't using lightweight_sync when those objects aren't streamed out.

The reason why I know this, is because I once created serverside projectiles with moveObject.

Link to post

Anyway, I will install this weekend 1.6, so I can release full project train directly after 1.6 comes out.

(If there will be another beta, it will be in 1.6 too.)

Another thing, the name of the resource(project train) sounds a little bit standard. So I might change it in something more creative. If you guys have any idea's for a better name, let me know.

Link to post
Nice work but sort of useless as it's compiled. :roll:

If it was open source you wouldn't understand a damn about it.

If you did(0.1% of the scripters), you would be able to make it on your own.

So I don't see a ***** reason why it shouldn't be compiled.

Link to post

That's really offending you know, I'm not an expert in trains or even scripting in general but I do know what I'm reading, otherwise I wouldn't comment at all, as you say your system are better than the others it's just foolish not being able to prove it by comparing the source code for example.

Link to post

Heh? Code better than others? That wouldn't make a difference in not be able to understand it, a foolish conclusion if you ask me. Better code would probably be better to understand. Anyway, is it so hard to bring the word "complexity" up? Or is it just me?

There is nothing offending about what I am saying, complex code is just not understandable for most people.

Anyway, be happy that it is free...

Link to post
  • 3 years later...

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...