biell / alti-server

Altitude game server wrapper
The Unlicense
13 stars 3 forks source link

[Script] Cross server teleportation #28

Open kevATin opened 4 years ago

kevATin commented 4 years ago

So I've been working on a script to test how the map transfer would work, and while I've been able to fix multiple errors, I'm stuck on these two.

#Cross server teleportation

MAP 'tbd_amrsn_test';

my($ip4)=`wget http://ipecho.net/plain -O - -q ; echo`;
my($ip)=$ip4;

HOOKS {
    'powerupAutoUse' => sub {
        my($e, $p)=@_;
        return unless($e->{'powerup'} eq 'Health');
        if($p->{'vaporID'} eq '26dc2a71-8d57-457e-b595-21328f3c17fb') { $ip='192.168.1.11'; }
        else { $ip=$ip4; }

        if($e->{'positionX'} eq '756' && $e->{'positionY'} eq '159') {
            server::send('serverRequestPlayerChangeServer', $p->{'nickname'}, '{$ip}:27280', ''); }
        elif($e->{'positionX'} eq '1600' && $e->{'positionY'} eq '81') {
            server::send('serverRequestPlayerChangeServer', $p->{'nickname'}, '{$ip}:27282', ''); }
    },
}

The output I'm getting is:

...

Ping of vapor.nimblygames.com successful.

Failed to load script tbd_script: syntax error at (eval 19) line 17, near ") {"

syntax error at (eval 19) line 18, near "'')"

So first, what's wrong with my script? And second, am I doing this ok at all or should I do something differently?

biell commented 4 years ago

Perl uses elsif, not elif for an else if construct.

Do you need to run that http get to get the IP address? I would be willing to add an /add serverPortal command similar to /add portal, but which ran /serverRequestPlayerChangeServer for you. But, you would have to hard code the IP address of the destination.

kevATin commented 4 years ago

Ah I see; but now it seems something else is broken. Upon starting alti+server the window seems to freeze, and the log output is:

Ping of vapor.nimblygames.com successful. Loaded script: tbd_script

I use that line to get my current external IP address. I've decided to do that because I don't have a static IP and want the script to keep working regardless of whether my IP or that of whoever else runs it, changes. As you can see I told the script to use the local IP when I am the player in question. I'm not certain if that is necessary, but when I played around with servers earlier I always got connected to the local IP (other players on my server were able to connect normally via my external IP), so my assumption is that Alti automatically tries to use the local route if possible. Hence it might fail if I forced it to use the external IP~ I might test at a later point if that's really necessary.

You mean "/add serverPortal" would then be part of the plus.txt? That would be nice, but it's also not like the feature is missing (at least once I get that script to work as it should). Hard coding an external IP is kinda a no go though; I'd like the server to be fully portable and work with what I've got without being bound to any IPs.

Also there's a good chance I'll try to move from activating the server transport via powerups to map positions, because positions seem to allow to give ranges where powerups are more restricted. So assuming you intend to link that command to powerups I'm not sure how much I'll end up using it.

biell commented 4 years ago

If you have to dynamically look up the destination each time, I am not sure I can help, you will need to put it into a script. Sorry.

kevATin commented 4 years ago

No problem, it's not like the script is that much more to type than plus.txt.

So do you have any idea what the reason for the script apparently freezing alti+server could be?

biell commented 4 years ago

you have a syntax issue, you need to use elsif, not elif.

kevATin commented 4 years ago

Ah no, I mean after fixing that; My current script is:

#Cross server teleportation

MAP 'tbd_amrsn_test';

my($ip4)=`wget http://ipecho.net/plain -O - -q ; echo`;
my($ip)=$ip4;

HOOKS {
    'powerupAutoUse' => sub {
        my($e, $p)=@_;
        return unless($e->{'powerup'} eq 'Health');
        if($p->{'vaporID'} eq '26dc2a71-8d57-457e-b595-21328f3c17fb') { $ip='192.168.1.11'; }
        else { $ip=$ip4; }

        if($e->{'positionX'} eq '756' && $e->{'positionY'} eq '159') {
            server::send('serverRequestPlayerChangeServer', $p->{'nickname'}, '{$ip}:27280', ''); }
        elsif($e->{'positionX'} eq '1600' && $e->{'positionY'} eq '81') {
            server::send('serverRequestPlayerChangeServer', $p->{'nickname'}, '{$ip}:27282', ''); }
    },
}

And my output is:

Using /usr/bin/geoiplookup (good) Using /bin/ping (good) Using JSON::XS (good) Using /usr/bin/lzma (good) Using /sbin/ip (good) Using /usr/bin/curl (good) Using /usr/bin/curl (good) Using /usr/bin/curl (good) Using /usr/bin/curl (good) Using DBD::SQLite (ok) Space reserved for maps: 4095 Ping of vapor.nimblygames.com successful. Loaded script: tbd_script

At which it just seems to freeze and not respond (the last line doesn't even make it to the console window).

biell commented 4 years ago

try adding a semicolon to end the hook statement:

HOOKS {
 ...
};
kevATin commented 4 years ago

Eh nope; same result as before

#Cross server teleportation

MAP 'tbd_amrsn_test';

my($ip4)=`wget http://ipecho.net/plain -O - -q ; echo`;
my($ip)=$ip4;

HOOKS {
    'powerupAutoUse' => sub {
        my($e, $p)=@_;
        return unless($e->{'powerup'} eq 'Health');
        if($p->{'vaporID'} eq '26dc2a71-8d57-457e-b595-21328f3c17fb') { $ip='192.168.1.11'; }
        else { $ip=$ip4; }

        if($e->{'positionX'} eq '756' && $e->{'positionY'} eq '159') {
            server::send('serverRequestPlayerChangeServer', $p->{'nickname'}, '{$ip}:27280', ''); }
        elsif($e->{'positionX'} eq '1600' && $e->{'positionY'} eq '81') {
            server::send('serverRequestPlayerChangeServer', $p->{'nickname'}, '{$ip}:27282', ''); }
    },
};

Using /usr/bin/geoiplookup (good) Using /bin/ping (good) Using JSON::XS (good) Using /usr/bin/lzma (good) Using /sbin/ip (good) Using /usr/bin/curl (good) Using /usr/bin/curl (good) Using /usr/bin/curl (good) Using /usr/bin/curl (good) Using DBD::SQLite (ok) Space reserved for maps: 4095 Ping of vapor.nimblygames.com successful. Loaded script: tbd_script

In a bit I'll set up a fresh server template to test and eliminate other sources of error, but the server works fine without the script as far as I can see.

biell commented 4 years ago

Is this the whole file? I copy and pasted this into a script on my server, and it starts without issue. Are you possibly hanging on your wget call? Maybe comment that one line out and temporarily just set the value to a static IP address for testing.

biell commented 4 years ago

One thing you may also find interesting in Xal's patches is that he added a lastPos field to the clientRemove entry, so you could store the location of a player when they leave, and respawn them at the same spot when they next join. I was scanning his pages, saw this entry, and thought that you might find it to be useful at some time.

fprintf commented 4 years ago

Hi guys, just wanted to chime in with some code tips/refactor suggestions on this script.


my $map_name;

MAP $map_name;

use LWP::Simple;
my $public_ip4 = get('http://ipecho.net/plain');

## TODO The portal map and the overrides as well as more advanced per-user settings should all come
## TODO from the database or a config file..
# Use a map of portals, generate from serverlist in repo
my %portal_map = (
    portal1 => { 
        x => 756, y => 159,
        dest => { ip => $public_ip4, port => 27280 },
        name => 'level 1',
    },
    portal2 => { 
        x => 1600, y => 81,
        dest => { ip => $public_ip4, port => 27282 },
        name => 'level 2',
    },
);
# Create a map of x,y -> portals for quick lookup
my %lookup_portal = map { 
    join(",", $portal_map{$_}{dest}{x}, $portal_map{$_}{dest}{y}) => $portal_map{$_}
} keys %portal_map;

# Override map will override server ip/port depending on user vaporid..
my %overrides = (
    # Override the IP for local user but not ports
    '26dc2a71-8d57-457e-b595-21328f3c17fb' => { 
        ip => '192.168.1.11' 
    }
);

HOOKS {
    'powerupAutoUse' => sub {
        my($e, $p)=@_;
        return unless($e->{'powerup'} eq 'Health');

        # Lookup portal based on x,y position
        my $pos = join(",", int($e->{positionX}), int($e->{positionY}));
        my $portal = $lookup_portal{$pos};
        if (!$portal) {
            return;
        }

        # Check overrides for vaporid (or whatever else you might want to lookup by...)
        my $dest = $portal->{dest};
        my $override = $overrides{$p->{vaporID}};
        if ($override) {
            $dest = { %$dest, %$override };
        }

        # Send server command with dest IP and port (possibly overriden)
        print "Sending $p->{nickname} to portal '$portal->{name}' at $dest->{ip}:$dest->{port}\n";
        server::send('serverRequestPlayerChangeServer', $p->{nickname}, "$dest->{ip}:$dest->{port}", '');
    },
};

Here is a refactor suggestion you might consider. You can pull public IP with LWP::Simple quite easily -- also created a lookup map to lookup portals based on position, this data could easily be put into a database and/or read from the serverlist config file you're using. This makes it very easy to add more portals without changing the code. You can also add additional overrides as needed although I think you will also need a secondary layer of configuration per-player to determine what server they're on and all that information.

The $map_name could be used to lookup a list of portals for that map/server within the database. With that change you could pretty much support all servers with this same code.