Curious: Stealth Mechanics


Pathfinder Online

101 to 150 of 187 << first < prev | 1 | 2 | 3 | 4 | next > last >>

If I've got 40k active connections, I hope I'm not limited to just one server. :D

Goblin Squad Member

Well, one Cluster anyway, right? PFO is planning to have a single "server" for all players.


They're also only going for an initial group of about 3k players too, right?

Even with a cluster, it can have almost any number of nodes.


https://goblinworks.com/blog/index.html#120711

Quote:
We're making a sandbox-focused game with a launch target of 4,500 players on a slow but steady growth plan.

So, they may start on a single server but they won't stay there, I promise you.

Goblin Squad Member

Buri wrote:
So, they may start on a single server but they won't stay there, I promise you.

I think I'll trust Ryan's promises more than yours.

Ryan Dancey wrote:
We want the largest possible servers. If we can get everyone into one, that's the ideal outcome.


That's not a promise. That's a goal. Besides, a single chassis can hold a single motherboard with 2 physical CPU sockets. Granted, that's 20 cores of CPU with 10-core Xeons and probably a couple hundred GB of RAM though you could scale out to multiple terabytes. You can even slap on a 10 gbps networking port. But, even that has limits. If the game keeps growing they're going to have to move to more than one server. That's a fact.

Eve is not one a single server, Nor is WoW, Guild Wars isn't, Matrix Online wasn't, SW:TOR isn't, etc. They can begin with a single server due to their player base but physics says they can't stay there if they want to keep growing unless they purposefully limit their growth to stay with the curve of technological growth that can go into a single box. Which, that would be dumb long term.

Goblin Squad Member

It's apples when you say "server" but it's oranges when Ryan says "server"?

Sounds like we're talking past each other.

Goblin Squad Member

Buri wrote:

That's not a promise. That's a goal. Besides, a single chassis can hold a single motherboard with 2 physical CPU sockets. Granted, that's 20 cores of CPU with 10-core Xeons and probably a couple hundred GB of RAM though you could scale out to multiple terabytes. You can even slap on a 10 gbps networking port. But, even that has limits. If the game keeps growing they're going to have to move to more than one server. That's a fact.

Eve is not one a single server, Nor is WoW, Guild Wars isn't, Matrix Online wasn't, SW:TOR isn't, etc. They can begin with a single server due to their player base but physics says they can't stay there if they want to keep growing unless they purposefully limit their growth to stay with the curve of technological growth that can go into a single box. Which, that would be dumb long term.

Well I believe you need to reffer to your definition of a single server. I believe you are correct that eve is not a single server, as in 1 box dosn't control the whole thing, but it is a single server in the sense that if joe and bob meet off the street, both have been playing eve for 2 years, and decide they want to team up, joe can fly to where bob is and team up with bob on his current character, Joe and bob can also immidiately on sight talk about events etc that happened with major guilds in the game because there is 1 world. The fact that it is multiple servers is transparent and unknown to the players, the servers are interconnected in a way that they might as well be 1 big server.

WoW on the other hand, is multiple servers in a very visible sense, this is what most people think of when they hear the term multiple servers. Joe and Bob meet up on the street find out they both play WoW, there is only a 1% chance that they happen to be on the same server, and thus to play together one must either make a new character on a server, or do an elaborate transfer making his character leave behind everyone he knew, as well as being warped off to a parallel world where different guilds are world known etc...

Goblin Squad Member

New Scenario:

Since the server(s) is(are) handling everything now, the client is receiving ~20kb of data per player every second in their sight range.

A player walks in to a populated zone.

That player starts experiencing horrible lag because there are 100+ people sucking up 2mbyte/s of bandwidth, and they are playing on a 500k(reasonable) connection.

There is a reason things like speedhacks, and wall hacks are so common in online games: The server can't handle everything. And it is not 'already handling everything' there is a small chunk of data to and from the server for every huge chunk of data the client deals with.

Until everyone is playing on viginticore processers with 100mbit connections, at a minimum, the server is not going to be on a technology level to 'run the game' for everyone on it's own.


If you're talking server as in a single instance of a virtual game world rather than a single physical box then that's not what I was talking about.

In the sense of a single world instance supported by multiple physical boxes, then, concerning the discussion about implementing stealth and client stuff I see even less reason why you couldn't do what I've been talking about.

In my defense, I'm an IT guy so if you say "hey, we're building this on one server" I instantly get an image of a single, physical rackmount unit. Apologies for the confusion over the distinction.


Valkenr wrote:

New Scenario:

Since the server(s) is(are) handling everything now, the client is receiving ~20kb of data per player every second in their sight range.

A player walks in to a populated zone.

That player starts experiencing horrible lag because there are 100+ people sucking up 2mbyte/s of bandwidth, and they are playing on a 500k(reasonable) connection.

There is a reason things like speedhacks, and wall hacks are so common in online games: The server can't handle everything. And it is not 'already handling everything' there is a small chunk of data to and from the server for every huge chunk of data the client deals with.

Until everyone is playing on viginticore processers with 100mbit connections, at a minimum, the server is not going to be on a technology level to 'run the game' for everyone on it's own.

Why would the client be receiving 20k characters of text for a single character? You'd need player id, name, class, stats, character model id, or multiple ids if each body part can be a different model, any viewable gear ids, ids to indicate other state like spell effects and "body conditions (injured, normal, etc)."

So, all that would look something like:

"{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }"

That is only ~564 bytes, less than 1k. A 20kb message would look like:

Spoiler:
{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }

I don't see why that much text would need to sent for a single character. You don't need their full bio, full inventory, all stats on items in their inventory, etc. All you should be getting is the minimum amount required by the client to display the character. Some extra data can be sent for "most common" operations but otherwise future data is gotten as necessary.

If 564 bytes is more accurate, that 500kbps (64kBps) connection can handle data about 113 characters at once, every second. However, once received that data is cached until the game tells it to go away or indicates an update to something, hp for example, but would look something like "{ 'characterid': 12341234, 'newHP': 12341234}" with some extra info to tell the client it's an event and some wrapper data, which is just a few bytes in most cases. This data is also most likely to be phased in as characters come within range rather than all at once unless you're first logging into the game, which can be hidden by a loading screen anyway. 100 people would take up 55kB leaving 9kBps. After the initial load of all 55kB of data, you have almost all that the very next second for control messages and updates and is pretty smooth sailing.

Updated math to account for the switch between bits and bytes.

Goblin Squad Member

Valkenr wrote:

New Scenario:

Since the server(s) is(are) handling everything now, the client is receiving ~20kb of data per player every second in their sight range.

A player walks in to a populated zone.

That player starts experiencing horrible lag because there are 100+ people sucking up 2mbyte/s of bandwidth, and they are playing on a 500k(reasonable) connection.

There is a reason things like speedhacks, and wall hacks are so common in online games: The server can't handle everything. And it is not 'already handling everything' there is a small chunk of data to and from the server for every huge chunk of data the client deals with.

Until everyone is playing on viginticore processers with 100mbit connections, at a minimum, the server is not going to be on a technology level to 'run the game' for everyone on it's own.

Well seriously, the servers aren't transmitting that much in Data even in a model where the servers are handling most inportant things. Things that are bandwidth intensive (i.e. graphics) are only on the client...the servers are only passing references to things the client needs to display...so you aren't talking huge volume...especialy if the server is just keeping track of inputs and outputs. I suspect you need to be passing larger amounts of data to the clients in order for them to handle some of the processing...... and really in terms of passing data...there are ALREADY online gaming services offering CITRIX like thin client services to pay FPS style games REMOTELY ON THIER HARDWARE. Admitedly they compress the Graphics a bit, so you aren't getting top notch resolution....but if they can handle that BANDWIDTH from a thicker client shouldn't be much of an issue.

In the specific example Buri is citing, data transfer between client and server should actualy be REDUCED because the server is only transmitting the data to the client that it actualy NEEDS to put up the pretty pictures on the screen. So bandwidth should definately NOT be an issue there.

Lets be honest, the ALOT of hacks in MMO's (not ALL but certainly many) have nothing to do with engineering, resource or technical limitations...they are the result of poorly designed, written and QA'd coding practices.

Goblin Squad Member

Buri wrote:
Valkenr wrote:

New Scenario:

Since the server(s) is(are) handling everything now, the client is receiving ~20kb of data per player every second in their sight range.

A player walks in to a populated zone.

That player starts experiencing horrible lag because there are 100+ people sucking up 2mbyte/s of bandwidth, and they are playing on a 500k(reasonable) connection.

There is a reason things like speedhacks, and wall hacks are so common in online games: The server can't handle everything. And it is not 'already handling everything' there is a small chunk of data to and from the server for every huge chunk of data the client deals with.

Until everyone is playing on viginticore processers with 100mbit connections, at a minimum, the server is not going to be on a technology level to 'run the game' for everyone on it's own.

Why would the client be receiving 20k characters of text for a single character? You'd need player id, name, class, stats, character model id, or multiple ids if each body part can be a different model, any viewable gear ids, ids to indicate other state like spell effects and "body conditions (injured, normal, etc)."

So, all that would look something like:

"{ 'character': 12341234, 'name': 'steve', 'class': 'wizard', { 'stats' { 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20, 'str': 20 } }, { 'characterModels' { 'head': 1234, 'body' 1234, 'arms': 1234, 'legs': 1234 } }, { 'inventory' { 'head': 12341234, 'body': 12341234, 'legs': 1241234, 'weapon1': 12341234, 'weapon2': 12341234, 'arms': 12341234 } }, { 'effects' { 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234, 'effect1': 12341234 } } }"

That is only ~564 bytes, less than 1k. A 20kb message would look like:

** spoiler omitted **...

Right...and in that case your not even talking about more advanced optimization techniques that could be used to reduce the amount of data sent in any one pulse dependant upon the congestion in a give area... things like Player Population in Area greater then X, reduce draw range by 30%, send reduced graphics detail for characters/objects.

That being said, we certainly don't know what sort of limitations PFO will actualy be up against...as I understand it, they are going with pre-built engines rather then thier own...so they'll have to deal with whatever limitations are already designed into the software they are using. But it's certainly NOT at all unfeasable for an MMO to be designed to handle many of the things being discussed here, if it was building it's own engine.

The one hack/issue I don't see them being able to get around is if Client X legitimately see's Stealthed Player A (i.e. X made it's "spot" role) then X can turn around and share that info out to Y and Z.

That one, I don't see an easy solution for.

Goblin Squad Member

GrumpyMel wrote:
... they are the result of poorly designed, written and QA'd coding practices.

Or... they are the result of cost-benefit analyses made by businesses that are trying to be profitable.

Speaking of, I bet there's a market for some middleware that can do all the things you guys seem to think should be easy to do...


GrumpyMel wrote:

Right...and in that case your not even talking about more advanced optimization techniques that could be used to reduce the amount of data sent in any one pulse dependant upon the congestion in a give area... things like Player Population in Area greater then X, reduce draw range by 30%, send reduced graphics detail for characters/objects.

That being said, we certainly don't know what sort of limitations PFO will actualy be up against...as I understand it, they are going with pre-built engines rather then thier own...so they'll have to deal with whatever limitations are already designed into the software they are using. But it's certainly NOT at all unfeasable for an MMO to be designed to handle many of the things being discussed here, if it was building it's own engine.

The one hack/issue I don't see them being able to get around is if Client X legitimately see's Stealthed Player A (i.e. X made it's "spot" role) then X can turn around and share that info out to Y and Z.

That one, I don't see an easy solution for.

Neither do I, because, logically, if player z casts fireball within range of player a then player a should be hit. The same is true if someone swings a weapon into that space, fires an arrow, etc.

That said, unless the client is modded or the player installs some overlay to accurate pinpoint where the character is, the stealthed player should still have the advantage.


Nihimon wrote:

Or... they are the result of cost-benefit analyses made by businesses that are trying to be profitable.

Speaking of, I bet there's a market for some middleware that can do all the things you guys seem to think should be easy to do...

That's an age old rub: business interest versus technical sensibility.

Concerning middleware, that market has a lot of really great solutions already which I'm sure do something very similar. One of the most popular of which is the Unreal Engine which has been supporting large numbers of people fragging each other since its inception. They *have* to support a lot of character models in real time otherwise they wouldn't have lasted so long.

Goblin Squad Member

Buri wrote:


That said, unless the client is modded or the player installs some overlay to accurate pinpoint where the character is, the stealthed player should still have the advantage.

Right, and I believe that is Ryan's point. Overlays, modded clients etc... are inevitable. Gameguard, warden etc.... all get bypassed all the time, and GW believes that allowing advantages to those who are able to bypass in a game focused around PVP, opens up the doors to all kinds of undetectable and unprovable cheating. Sure a human can note "OK that wizard just cast fireball into the middle of a field onto the stealthed rogue, he has to be cheating", but how do they determine that without also risking banning people who out of dumb luck casted into an empty field to check for stealthed characters.

Goblin Squad Member

Buri wrote:
Concerning middleware, that market has a lot of really great solutions already which I'm sure do something very similar. One of the most popular of which is the Unreal Engine...

And the Unreal Engine doesn't get hacked to reveal stealthed players?


I'll say that modded clients aren't inevitable. There are several techniques to insure your code libraries haven't been tampered with. You can be relatively sure the client you execute is what you sent out. And when I say relatively sure, it's in a scientific sense, not in the sense you *think* that duct tape will hold two pipes together.

Overlays would have to operate on exposed client data to be able to sync up properly. Otherwise, all it would be able to do reliably is provide a list of characters and their x,y,z coordinates which isn't all too helpful unless that's displayed on the screen, or, again due to some exposed data by PFO, there is some intermediary conversion they can hook on to.

You could do this memory scans to get your current position and extrapolate from there but even there, yet again, there are memory randomization techniques to deal with this. That technology is used by a few different web browsers and operating systems, actually. Memory sandboxing is a well-known technique at this point in computer science.

The thing is, it all depends. However, back to what I said is that for a game to implement stealth does not absolutely require all clients around that stealthed character to know where it is. That just doesn't make sense.

Goblin Squad Member

Nihimon wrote:
GrumpyMel wrote:
... they are the result of poorly designed, written and QA'd coding practices.

Or... they are the result of cost-benefit analyses made by businesses that are trying to be profitable.

Speaking of, I bet there's a market for some middleware that can do all the things you guys seem to think should be easy to do...

Sorry, I have a family....I work in the Business Services vertical where I can make a decent living. Entertainment Vertical (i.e. Game Dev's) is notorious for over-working and under-paying thier Tech workers. My impression is that most of the guys working in it (as opposed to the owners/investors) are doing it for reasons other then compensation and working conditions.

I actualy looked into it a few years back, I would have had to take somewhere between a 30%-50% pay cut for the equivalent position in a Game Developer.... not going to happen. Maybe if I was 25 and single.

Goblin Squad Member

Onishi wrote:
Buri wrote:


That said, unless the client is modded or the player installs some overlay to accurate pinpoint where the character is, the stealthed player should still have the advantage.
Right, and I believe that is Ryan's point. Overlays, modded clients etc... are inevitable. Gameguard, warden etc.... all get bypassed all the time, and GW believes that allowing advantages to those who are able to bypass in a game focused around PVP, opens up the doors to all kinds of undetectable and unprovable cheating. Sure a human can note "OK that wizard just cast fireball into the middle of a field onto the stealthed rogue, he has to be cheating", but how do they determine that without also risking banning people who out of dumb luck casted into an empty field to check for stealthed characters.

I DO get Ryan's point about the information disclosure aspect of it where if 1 player legitimately detects the stealthed character then he can share that info out of band with all other players. That makes sense to me.

What I didn't get is what he seemed to be implying in his second post (The one which Buri quoted) which was that info about a stealthed character had to be sent to all clients EVEN when NO ONE had legitimately detected that character. I just don't understand why THAT would logicaly have to be the case.

Also, I think just because there is an information disclosure vulnerability doesn't mean you should allow players who haven't "detected" that character from INTERACTING with that character however they choose. THAT is something the server SHOULD be able to control.

My preference, as I mentioned before, would be a pseudo-stealth mechanic. "Stealthed" characters ARE revealed to the clients and rendered for EVERYONE with a transparent/greyed out texture (thus cheaters/hackers get no benefit from the information disclosure vulnerability). However, the server tracks individualy which players in the vicinity have "spotted" that stealthed character. "Spot" checks could be implimented as active abilities with cool-downs for players so that should keep resource usage in check. The server limits the way "stealthed" characters can be interacted with by players that haven't "spotted" them...no direct attacks, targeting or interaction.... I'd even say it was reasonable provide stealthed characters partial damage mitigation against AOE attacks (after all the attacker doesn't know precisely where they are to target for effect and presumably the stealthed character is keeping low and using cover as much as possible to avoid detection...which would naturaly help against many types of area attacks). In this way, stealth acts as a sort of "buff" and spot/perception as a selective debuff.

In this way you preserve some of the tactical utility of stealth without making it absurdly over-powered (i.e. near perfect invisability out to 5 ft) .... and you do so without giving the hackers any real advantage... nor should there be much in the way of resource or engineering implications from the system. Technicaly it shouldn't be that different in implimentation then the standard "buff" system that most MMO's use... the only wrinkle is that your are being selective about who that buff is in effect against. Basicaly you are creating an array of "Spotted" stealthed characters for each player...and when one of those characters goes to apply an ability/effect to a "Stealthed" target the Server checks to see if that target is in the array before determining how/if to apply the effect. Doesn't sound like that difficult a design to impliment to me.

Goblin Squad Member

Buri wrote:

I'll say that modded clients aren't inevitable. There are several techniques to insure your code libraries haven't been tampered with. You can be relatively sure the client you execute is what you sent out. And when I say relatively sure, it's in a scientific sense, not in the sense you *think* that duct tape will hold two pipes together.

Overlays would have to operate on exposed client data to be able to sync up properly. Otherwise, all it would be able to do reliably is provide a list of characters and their x,y,z coordinates which isn't all too helpful unless that's displayed on the screen, or, again due to some exposed data by PFO, there is some intermediary conversion they can hook on to.

You could do this memory scans to get your current position and extrapolate from there but even there, yet again, there are memory randomization techniques to deal with this. That technology is used by a few different web browsers and operating systems, actually. Memory sandboxing is a well-known technique at this point in computer science.

It is still bypassable. Eventually it can be run, if there is some trait that the client uses to figure out where the information goes, there is some way the game can know. Also it isn't just the memory, you also have to watch the packets. In everquest, show EQ worked entirely by decrypting the packets, and continued to work as the developers continued to up the encryption until the point where making strong enough encryption meant killing the performance of the game. I'd imagine another counter for any checks would be a rudimentary rootkit. For years major rootkits have been able to modify a file, yet when any program scans or checks the file, it returns the unmodified legitimate result. Physical access to the host machine when the game isn't running, is an unbeatable security hole.

Quote:


The thing is, it all depends. However, back to what I said is that for a game to implement stealth does not absolutely require all clients around that stealthed character to know where it is. That just doesn't make sense.

I agree, if they can develop stealth in a way that nothing needs to be transmitted to the client or any allies or potential allies, then it is plausible. That being said GW hasn't said stealth won't exist, just that it won't necessarally be nearly as ideal as people hope and that they would rather not impliment stealth at all vs impliment it in a way that cheaters can bypass it. This being said, in discussion of stealth we should assume stealth/invisibility's existance as a possibility and not as a given.

Goblinworks Executive Founder

Buri wrote:

I was thinking last night about the 10k calculations number that Valkenr mentioned and why that really didn't phase me. Here's why.

On a single-core, 1 GHz CPU that only represents 0.00001 of the processors time. Given that most modern CPUs are in the 2 - 3 GHz range, that comes to 0.000004 (there's an extra zero in there in case you don't see it). Also, considering this is most likely done with stat checks (stealth vs perception) which is just boolean based greater than comparisons, this can be split into an asynchronous job. If it is, let's assume it's spread out against just 4 cores, because really, if you're running an MMO and running anything less then you're just not doing it right. I can almost guarantee you that an average WoW server is a dual socket, 10-core a piece Xeon behemoth.

That comes to 0.000001 second. 6 cores: 0.0000007. 8 cores: 0.0000005 quadrillionths of a single second. These are minuscule fractions of a second. On a single server. For 50 people. The speed of the human nervous system is much, much slower than this only reaching speeds of milliseconds. To scale it out, the time it takes your finger to tell your brain you're touching something, we could do these calculations about ten thousand times.

Let's assume godlike RAM timings for a 3 GHz clock speed: 5:5:7. Each character needs one bit for every other character: At 4500 characters, that's ~2 MB of RAM per character, or ~10 GB of RAM for all of them, just tracking who can see whom. You can cut that down by limiting the number of characters considered in comparison to each other, but if you use less than one dedicated bit per player there's overhead involved in determining where that bit should be. Assuming that using N^2 RAM to track stealth isn't acceptable, every tick something like the following needs to happen, for every (stealth?) player:

GetPlayerStealth(
LookupPlayerOffset
ReadMemoryValue
)
GetPlayerPerception(
LookupPlayerOffset
ReadMemoryValue
)
CompareStealth( (first other player)
BooleanCompare
LookupPlayerOffset
WriteMemoryValue
)
CompareStealth( (second other player)
BooleanCompare
LookupPlayerOffset
WriteMemoryValue
)
...
Each of these can be in their own thread, but each one requires from 12-16 core cycles to complete, and you have a metric s+$@-ton of memory reads and writes. You can reduce the number of memory accesses by making changes to stealth take longer (real time) to take effect, or by making changes to stealth more expensive (technologically), but you have to either write or (read, compare, optionally write) every used value every tick.

If, on the other hand, there's just one stealth flag per character, you only need to write when it changes.

tldr; treating stealth as a condition which exists on the stealthy character is significantly easier than treating it as a relationship between two characters, and becomes more so as population increases.

Goblinworks Executive Founder

Buri wrote:
I'll say that modded clients aren't inevitable. There are several techniques to insure your code libraries haven't been tampered with. You can be relatively sure the client you execute is what you sent out. And when I say relatively sure, it's in a scientific sense, not in the sense you *think* that duct tape will hold two pipes together.

Describe how you can tell what code is running on a remote computer, using only data provided by that, code which may be a response to a query. Assume that the suspect code is either a given binary or produced by somebody who has full access to that binary.

If you used memory randomization, an early hack would remove that, or (same thing) disable the RNG used to randomize it. Any encryption used on the packets can be intercepted from memory either prior to encryption or after decryption. Every individual measure used to prevent cheating can be defeated in isolation from all others.

I suppose you could release major patches which substantially change how the code works, and also change how communications are handled, and hope to be faster at releasing what amounts to entirely new code than the cheaters are at breaking it. That requires keeping the people testing the code from leaking it, and also having a rapid test cycle. If you can describe how to perform good quality control in a short time with few people, I will fund your startup for cheap, effective third-party quality control.


@Decius Apologies for quoting in your response but there's a lot of badwrongfun going on in your first post that needs to be addressed individually.

Quote:

Let's assume godlike RAM timings for a 3 GHz clock speed: 5:5:7. Each character needs one bit for every other character: At 4500 characters, that's ~2 MB of RAM per character, or ~10 GB of RAM for all of them, just tracking who can see whom.

Again, my IT mindset sees the word bit and things a single 1 or 0, which is a valid boolean expression. One bit, as you say, per character is 4,500 bits, or 523 bytes. Depending on the language implementation of the boolean type, a whole byte may be used to encode a value which would equate to 4.4kB for everyone on the server. This is far from taxing even on "poor" machines based on today's standards. My Packard Bell with it's 4 MB of RAM back in the early 90's could handle that with ease. I don't know why you'd want to store a matrix for each and every character about the other. This would be intentionally piling on more data. Performing boolean operations are small enough to make such caching unnecessary. This would only be advantageous if all the data that needed to be collected would take longer to compile individually than a single round trip to the RAM. In this case, it wouldn't.

You can cut that down by limiting the number of characters considered in comparison to each other Which you should be. Stealth checks should only be done on stealthed characters., but if you use less than one dedicated bit per player there's overhead involved in determining where that bit should be. This operation is masked by every operating system even approaching modern. You couldn't pass a college level OS course without such a feature. In fact, in Windows, this is done so well, it's essentially a free operation. If it weren't, every application in existence would be brought to its knees by having to perform memory scans on each and every operation, which simply doesn't happen and adding more RAM would actually slow down machines because there is more addresses to sift though which, again, doesn't happen. Assuming that using N^2 RAM to track stealth isn't acceptable, I have no idea where you got where anything near this would be necessary. Occlusion culling should be taking characters that don't factor into these equations out of the picture a long time ago by time your'e getting to things like a skill check where you're doing stealth versus perception. every tick something like the following needs to happen, for every (stealth?) player:

GetPlayerStealth(
LookupPlayerOffset
ReadMemoryValue
)
GetPlayerPerception(
LookupPlayerOffset
ReadMemoryValue
)
CompareStealth( (first other player)
BooleanCompare
LookupPlayerOffset
WriteMemoryValue
)
CompareStealth( (second other player)
BooleanCompare
LookupPlayerOffset
WriteMemoryValue
)
... No clue what these are here for. A new comparison method does not need written for each successive check. I think you have the perception too that the game would need to ask the server each and every time what a character's stealth result is every time a check is made. This isn't true. You would cache this data for successive results. Also, offsets are irrelevant here. If the offset of two characters is such that character a can't "physically" see character b in the game world then this check is obsolete at this stage and should part of the culling step I mentioned in earlier posts and earlier in this one. If this check is being performed, it should be assumed the characters do have line of sight to each other, one is stealthing and we simply need to see if the other is skilled enough to spot him. An actual game method would look something like this:

bool CanViewOtherStealthedPlayer(Player player)
{
if(player.IsStealthed != true)
return true;

if(player.Stats.Stealth < this.Stats.Perception)
return true;

return false;
}

"This" refers to the local player object instance (the character of the player on this client). This method would be part of the IPlayer interface so each and every Player object, which itself is part of the IGameEntity interface as well, would be guaranteed to have it. So, after the culling step by the server, the client should have a result of visible objects to be rendered and would look something like:

void RenderObjects(IGameEntity[] entities)
{
var objectsToRender = new GameEntity[]

for(var index = 0; index < entites.Length; index++)
{
var entity = entities[index];

if(entity is Player)
if(!LocalPlayer.CanViewOtherStealthedPlayer(entity))
continue;

[..] << other checks for other types of objects that may not need to be rendered

objectsToRender.Add(entity);
}

RenderThing.Render(objectsToRender);
}

The above is an efficient way to handle however many players the clients gets data about.

Each of these can be in their own thread, but each one requires from 12-16 core cycles to complete, and you have a metric s@*$-ton of memory reads and writes. This is common. In fact, you'd be hard pressed to find an application that doesn't do this. If you're not hitting RAM for data, then you're hitting another storage device, which I can guarantee you is much slower. Whenever an application says "Get me this data," unless it's an explicit file system operation, the local processor cache is checked first followed by RAM. If you're not hitting RAM or another storage device then you're doing nothing as the CPU itself is not a repository for data. You can reduce the number of memory accesses by making changes to stealth take longer (real time) to take effect, or by making changes to stealth more expensive (technologically), but you have to either write or (read, compare, optionally write) every used value every tick. I mentioned that checks should be instanced so new skill checks are done every 5 seconds or so. However, yes, the check will need to be made every tick. Using the above, though, the client could implement a "do not render" buffer that gets cleared out every few seconds so that even if a character was given to the client to render, if it existed in this buffer, then it's not rendered at all. This would negate the skill checking but would yet be another check so it would equal out performance wise. Refer to my earlier post about execution times to see how quickly this check is done.

As per the scheme you're asking about, SW:TOR does extensive checking to ensure the validity of code before the game will run. A system you're asking for will look something like the following:


  • An incoming launcher connection comes into the game server. The server will have access to the certificate used to make the HTTPS connection and can verify the validity of the certificate.
  • Once verified, a challenge is issued to the launcher. The correct response to this challenge is calculated in real time, not stored, with a hash of the response also sent back to the server.
  • The server compares the response to the challenge as well as the hash, computes its own hash and verifies the challenge result is valid.
  • Next, the launcher performs a real time check on all local game binaries and sends this information to the server. This is more than a hash and is more of a thumbprint. The server compares this against its database and issues a list of any applicable updates. In the case of file corruption, the entire binary would be a required download.
  • After the launcher downloads and patches the game, another check is made and passed to the server. The server issues a command letting the launcher know everything checks out. This let's the launcher finally launch the game.
  • If the launcher does not respond correctly to the challenge or is out of date itself, the server issues a launcher update instead and then things proceed normally from the above.
  • Connections made with an invalid or bad certificate similarly issue a launcher update.

In such a system the game has to be coded in such a way to only accept start up commands from the launcher. The game can verify the authenticity of the launcher in the same way the launcher can verify the file versions of the game via a challenge/response mechanism where the response is calculated and not stored. The acceptable algorithms by which these challenges are issues is kept in sync via the update process and should be changed regularly and actually aren't that difficult to change.

I've seen these types of authentication schemes in place and all that really only takes a fraction of a second up to a couple seconds if you're experiencing horrible network lag.

Goblinworks Executive Founder

@Buri
I was considering the resource use from an operation standpoint, not a function standpoint. I wasn't implying that those operations should be coded separately, only that they needed to be executed separately. I didn't expect that the OS overhead was so much greater than the actual execution time so as to render things like looking up a value for each online character meaningless in comparison. I suppose it's technically possible to have that much cache, but at that point you're fudging the line between memory and cache.
EDIT: On checking further, you've moved the issue up a level: At some point you create a matrix of which characters are interacting with each other, and each operation that compares all characters to each other (stealth and collision detection come to mind) traverse this matrix and only compare characters who aren't distant from each other.

I do like your discussion of the cryptography. The cracked launcher uses the exact same code for challenges and responses that the real one does, and compares the real files and sends the real thumbprint to the server. Then it launches a different executable. The hacked game executable accepts start up commands from the cracked launcher.

Goblin Squad Member

@Decius, signed manifests are relatively secure, and a good way to ensure that the client is running the software you distributed. Unless the hacker gets access to your private key, they're probably not going to spoof it. So, you can be relatively certain that your client is running, but you have no idea what else is running, and that's where the trouble lies.

@Buri, you keep moving the goal posts.

Buri wrote:
... for a game to implement stealth does not absolutely require all clients around that stealthed character to know where it is.

Yeah, no one here ever argued otherwise. What we said was that's not the way it's done in the current standard model, and to do it the way you suggest would require significantly more server resources than the current standard model.

Goblinworks Executive Founder

Nihimon wrote:

@Decius, signed manifests are relatively secure, and a good way to ensure that the client is running the software you distributed. Unless the hacker gets access to your private key, they're probably not going to spoof it. So, you can be relatively certain that your client is running, but you have no idea what else is running, and that's where the trouble lies.

-I- know what I'm running, but that doesn't mean anyone else does. The hacker doesn't need your private key to spoof running the real software- any more than I need it for normal operation. The right metaphor isn't 'man in the middle', it's 'psychic who knows everything Alice does and controls all of Alice's actions and communications'.

Goblin Squad Member

@Decius, you're right. I consciously avoided the hacker mentality all my life (taught myself to program at 13, been doing it professionally since I was 18). I still get caught up sometimes forgetting to keep in mind that the person sitting at the client is not trusted. It sucks.

For the record, I would love to see a system where all necessary processing occurred on the server, so that we could have great stealth mechanics. I just don't believe the only reason we're not going to get that is because Goblinworks is cheap and lazy, and I find it kind of offensive that people keep making that argument in a roundabout way.


@Nihimon

The arguments have been about how it would be a great undertaking to move a stealth check to the server rather than the client. This simply isn't true and is what I've been working to show. It doesn't help that people aren't being precise in their language in what has become a technical discussion. For example, saying that a bit x 4500 somehow equals 20 MB per user makes no sense mathematically unless you are intentionally implementing an n^2 storage method which would simply be insane. Similarly, saying that you'd need an amount of text the length of a short story just for a client to render a single character is wrong as well. Explaining how and why these are wrong and giving examples of what is more accurate is what we've been talking about over the last several posts. Though, you are correct that the assertion I made hasn't been challenged directly.

@Decius

The challenge/response mechanism should probably also provide something of the same sort of thumbprinting technique that is used to verify the state of game files. If the thumbprint is off then a launcher update should be issued.

Also, your application can be sure of what's being loaded due to signing, as Nihimon mentioned. To critique his point that you can't know what's running, Windows 7 does support signing even on injected DLLs which removes a large avenue of attack.

Other avenues of attack such as using a debugger takes hours or even days to figure out what's going where. When things get compiled down to assembler, there are no more variable names. It's all converted to memory address pointers, which is really all a variable name is anyway. So, changing the randomization technique every now and again will change which sections of memory story data which will break any memory scraping application. Buffer overruns are easy to ward off with proper bounds checking and initialization. You should declare variables to only hold an amount of data that is reasonable for the function of the variable. If a string comes into the application that is several KB or MB when it should only be a few bytes, then something went wrong and the application should catch that.

After injection and memory scraping, all a hacker is left with is data the application itself exposes which shouldn't be that much and can usually be capped off easily enough in future updates. Secure computing is possible and while it takes a fair amount of due diligence on the company's part it should be an essential item on the QA checklist.

To recap: we've gone from my assertion that it doesn't, in fact, take lot of resources to do stealth checks on the server to me showing how much actual memory it would take (small) and how much processing time it would take (also small) and now we're questioning the validity of the code running in the first place. With what I've described in my more lengthy post and this one I think this has been adequately addressed as well.

Goblin Squad Member

Buri wrote:
It doesn't help that people aren't being precise in their language... saying that a bit x 4500 somehow equals 20 MB per user makes no sense mathematically...

It was clear to me that Decius wasn't saying that.

Decius wrote:
Each character needs one bit for every other character: At 4500 characters, that's ~2 MB of RAM per character, or ~10 GB of RAM for all of them...

Perhaps it's not his failure to be precise that's the problem here.


It's still an unnecessary thing to do.

Goblin Squad Member

And it's still true that no one was saying it was "necessary". Only that it was "costly" to do it another way, whether that cost is in server resources or development resources.


And that cost is very small in computational terms especially if you're processing against a server cluster versus a single physical box. The development cost is actually halved in both time and effort as the developers don't have to worry about client data where it's not necessary and can simply focus to resolve things normally on the server.


Ryan, Page 1 wrote:
In general, being "hidden" is an all-or-nothing proposition

i.e. necessary

Goblin Squad Member

1 person marked this as a favorite.
Nihimon wrote:

@Decius, you're right. I consciously avoided the hacker mentality all my life (taught myself to program at 13, been doing it professionally since I was 18). I still get caught up sometimes forgetting to keep in mind that the person sitting at the client is not trusted. It sucks.

For the record, I would love to see a system where all necessary processing occurred on the server, so that we could have great stealth mechanics. I just don't believe the only reason we're not going to get that is because Goblinworks is cheap and lazy, and I find it kind of offensive that people keep making that argument in a roundabout way.

Nihimon, I don't think anyones accusing GW of that, at least I know I'm not. Frankly they are a brand new studio...so right now they have no reputation, good, bad or indifferent. They'll establish that reputation one way or another once they've released something.

I don't think recognizing that they've a limited budget is the same as accusing them of being "cheap" in some backhanded fashion. Like 95% of organizations out there, they've got the resources they've got to make the project work. However there is a big difference between something can't be done because it doesn't fit within a projects budget and claiming that it's technicaly impossible to do at ALL with today's technology. There may be a few things discussed here that there aren't any real good solutions for at the moment (I think the Info Disclosure things where if 1 client learns something it can pass that along to other clients which aren't supposed to know it, is a good example) but it's not like preventing wall hacks is asking anyone to design a warp engine.

I do stand by the statement that MOST (definately not ALL) hacks/exploits that exist in MMO's today are the result a bad design and sloppy coding/QA. If you look at the work processes involved in producing many (not ALL) MMO's, especialy those owned by some of the big publishing houses, it's not hard to understand why. An MMO is a really compilcated type of application to build. Many of the larger publishers DO skimp on the tech budgets for production in comparison to marketing budgets. The industry as a whole has a tendancy to not pay competitive wage's for Tech's when compared to other software verticals... so thier not always getting the best talent. On top of that companies will often hire people who really are too junior for a project in order to save even more on salary. Then they turn around and have those people work 70+ hour weeks (usualy without paying OT) from near the start of the project in order to save even more on labor costs....even the best people aren't going to be able to do quality work under sustained conditions like that. On top of that may farm out important pieces of the code to off-shore "sweat-shop" coding houses. Because of these sorts of conditions they tend to have pretty high turn-over rates...which of course causes it's own set of problems with project continuity. Then they cut short QA because of budget & schedule over-runs.... and the same companies turn around and wonder why there are so many bugs & exploits found in thier code.

Now, there ARE studios that aren't like that....and hopefully GW will be one of them.... but from everything I've read and heard through the grapvine those sort of practices are running rampant in many companies in the industry right now. So yeah, it's no surprise you are going to get hacks/exploits/bugs when your dependant on those sort of work processes. The dupe exploit that you find in some game you are playing tomorrow isn't likely the result of not knowing how to design a positronic brain to prevent it....it's because the poor guy who was coding it just put in a 16 hour shift on a Sunday and he forget to comment out some section of code he was using as a short cut to test something.

Goblin Squad Member

Nihimon wrote:
...I would love to see a system where all necessary processing occurred on the server, so that we could have great stealth mechanics. I just don't believe the only reason we're not going to get that is because Goblinworks is cheap and lazy, and I find it kind of offensive that people keep making that argument in a roundabout way.

For the record, I never even realized that was an interpretation of my argument. If that is what I thought, I would not still be here. However, also being in the computer/programming domain, I know that the way things are commonly done is not always the only...or even the "best" way to accomplish a goal. Industries have default methodologies, often implemented for good reason, that do at times stifle innovation.

I have admitted many times that I do not code MMOs, but the argument used to argue against things such as meaningful darkness and "improved" stealth have dire logical extensions:

People can hack the client to remove the detriment of darkness. People will hack the client to remove the detriment of darkness. Removing the detriment of darkness causes suffering to those who play the game as intended. Causing suffering to those who play the game as intended is bad. Therefore, the detriment of darkness should not be added.

People can hack the client to remove the benefits of stealth gained by others. People will hack the client to remove the benefits of stealth gained by others. Removing the benefits of stealth gained by others causes suffering to those who play the game as intended. Causing suffering to those who play the game as intended is bad. Therefore, the benefits of stealth gained by others should not be added.

This direction of argument goes on and on...anything interpreted by the client should actually be removed...if as suggested the client can be hacked and cheated. This obviously does not lend to much of a gaming experience then. However, since everything extends from:

People can hack the client to remove x. And removing x is bad.

There are actually three solutions that become apparent. Make removing x not bad, never add x (this is the solution offered), or the solution Buri and I were advocating, make the client unhackable. Of course, as argued by Decius, there is no way to make a program totally secure.

The problem however is not that the client needs to be secure, the problem is that the client has information that should not be available to the player. The nature of the hacks in question is that players are accessing this information "illegally". The solution: Do not give the client information the player should not have.

Of course, this leads to the whole group of problems mentioned by others, such as processing time when groups of people suddenly appear next to you. This question however, seems more solvable than any of those above. For instance, off the top of my head, two solutions (that perhaps can be used in tandem for a third):

1) Make the client the top down processing to the server bottom up input. In this model, the job of the client is to store prototypes (which fits with my previous argument that the client should only process art assets). So, your buddy John the Ripper has a prototypical image stored on the client. This image is a representation based upon your average (or maybe simplify it to last) interaction with John. If the last time you saw him he was leather armor and has daggers, then that is the stored prototype of him. When he suddenly appears next to you, the client recalls the prototype and then corrects the data at a lower priority. He may be carrying a rapier this time...as soon as possible the client will correct the error. This would work with a priority system. The first thing the server would have sent when the 100 people instantly appeared next to you was the names and locations of each. The prototype system recalls stored prototypes of those you have previously (or often) interacted with. Next it sends basic information about each character, your client adjusts as necessary, then more detailed information...and further adjusts. One of the most basic bits of information would be hostility, if an outlaw appears next to you (or an aggressive PvE mob) no matter what they look like, they will flash red or show red on your minimap...or whatever method is used to denote hostile entities. This allows you to quickly respond, even if you are not totally certain about the nature of the attack. It is important to remember that the latency everyone seem concerned with is still fractions of a second...most data could be corrected before it is even noticed to be incorrect. This is the way many propose our brains work (think of the continual massive influx of data and the processing requirements there).

2) Similar to above but without specific prototypes, just use a generic medium sized male "shadow" or outline that can be responded to until more detailed information arrives. Again, remember the latency we are talking about is fractions of seconds.

3) A combination of the two would store prototypes of anyone you have added to your friends (or contacts) list. Even if people hack the client and share these prototypes...who cares. It offers no benefits. Either route, the objective is to maximize the amount of interpretation the client can do while minimizing server-client traffic. All the while, necessarily withholding information the player should not have from the client.

I apologies for the wall of text, I just did not want to be pushed into the category Nihimon suggested (not sure if I was one who gave him that impression or not...either way no worries, the error was my failure to express myself correctly), especially as I am one of the loudest and most consistent arguer for these things. I do realize I might only be arguing with a philosophical rationale to what is really GWs desire to not have these things because they conflict with their image of the game. I have trust that GW is doing their most to build a good game, it is after all, in their best self interest to make us, the customers as happy as possible. Since they seem to be asking for discussion, I am simply offering it.

Goblin Squad Member

I'm not trying to shout anyone down from making suggestions or discussing technical limitations. I wouldn't be surprised if some of the suggestions proposed here actually have some relevance to a "next generation" MMO engine that solves these problems.

However, I don't expect a "budget MMO" to develop a "next generation engine", especially when they've already announced they're going with a third party engine.

I apologize for using a broad brush to characterize the arguments being presented here in such negative terms.

Goblin Squad Member

No worries, I was not offended. Even if you were referring to me specifically, it was based upon my failure to express myself well...I have learned to trust your opinion Nihimon, so if I came across as sounding such, my apologies to GW and the community.

Sure, I understand their middleware may not allow for more creative or "next gen" implementation. I can only discuss solutions/implementation for the concerns they do mention.

Goblinworks Executive Founder

Buri wrote:


For example, saying that a bit x 4500 somehow equals 20 MB per user makes no sense mathematically unless you are intentionally implementing an n^2 storage method which would simply be insane.

I did make a math error- specifically, I think I failed to convert bits to bytes, since all my numbers were off by about a factor of 8.

Basic assumption: Each character can be stealthy or not independently to every other character. That leads to n^2 possible states, requiring n^2 memory to contain all of those states. Each bit can and must be evaluated independently, requiring n^2 time to evaluate. You can reduce n by culling, but cheap culling involves other tradeoffs. In any case, each check for stealth requires a read, a compare, and a write operation; combined with the physical properties of the hardware, that puts a hard limit on speed and prevents them from running in parallel- unless the overhead makes the actual operations trivial. In that case, 'wait, what'?

As far as security goes, it simply doesn't matter what thumbprinting method is used. The cracked launcher will take the thumbprint of the real one, and send that to the server. The cracked executable will either not check a thumbprint, accept the thumbprint of the cracked launcher, or be launched by a program that modifies itself to have the correct thumbprint immediately. I suppose you could have the client send some kind of report to the server about itself, which might be very difficult to catch and spoof if it had a different response each time.

As for memory address hacks, the enemy has access to the assembled code. Assuming you don't make major changes to the uncompiled source, there will only be superficial differences in the compiled code. It becomes straightforward to compare the before and after versions and determine what changed. Similarly with the data transmitted- with the added bonus that you can perform crypto on packets, which you can't do on memory. The attacker, however, still has the over-the-shoulder attack, and anything computationally intensive will drive off customers.


I wouldn't store each and every possible permutation for each character in the game world to each other. The reason being is that all these checks becomes useless at the addition of a single new character or the loss of one which would require each and every check-set to be rebuilt as it would also with two new characters simply coming within line of sight range to each other. You can plan for 4,500 sets to ultimately exist but the climb to that number will get slower and slower and the memory serialization/deserialization for what equates actually to a 10 GB block of RAM will be very, very slow, indeed. It also doesn't make sense for the game to evaluate stealth information for you and I if our characters are at their farthest possible distance from each other which would be at polar opposite ends of the game world. It wouldn't make sense for the game to evaluate this information even if we're in the same town but in different buildings. If we can't see each other, the game shouldn't be doing these checks, period. Suffice it to say, this is unwieldy and unmanageable.

Each game area should be independent of each other and use dynamic loading to make the transition from area to area transparent to the user. This is a common loading scheme in MMOs now. As such, assuming an even distribution of characters, the worst possible real time check becomes (4,500/number_of_hexes)^2, which comes to 309 bits, or 39 bytes. With a single pass of line of sight culling, I would assert the upper 75th percentile would be moreso in the range of 200 characters for a densely populated area such as a busy market. A real time check will consume a total of 200^2 bits, or 4.9 KB. This scheme is actually required to let the game grow smoothly.

On security, you seem to have a view that an application can be altered at will. This isn't so and is actually a considerable undertaking. Code signing would catch these inconsistencies in the event such changes were made, successful or not, and prevent execution of any modified code. It's just how code signing works.

http://en.wikipedia.org/wiki/Code_signing

First sentence wrote:
Code signing is the process of digitally signing executables and scripts to confirm the software author and guarantee that the code has not been altered or corrupted since it was signed by use of a cryptographic hash.

I don't know why memory attacks would be an issue where stealth is concerned as all the hacker would see is a 1 or 0. Unless it's the *only* 1 or 0 in the entire application, this will be useless. All they will see is the memory address. This doesn't provide any context and data without context is also useless.

Goblinworks Executive Founder

Buri wrote:
I wouldn't store each and every possible permutation for each character in the game world to each other. The reason being is that all these checks becomes useless at the addition of a single new character or the loss of one which would require each and every check-set to be rebuilt as it would also with two new characters simply coming within line of sight range to each other. You can plan for 4,500 sets to ultimately exist but the climb to that number will get slower and slower and the memory serialization/deserialization for what equates actually to a 10 GB block of RAM will be very, very slow, indeed. It also doesn't make sense for the game to evaluate stealth information for you and I if our characters are at their farthest possible distance from each other which would be at polar opposite ends of the game world. It wouldn't make sense for the game to evaluate this information even if we're in the same town but in different buildings. If we can't see each other, the game shouldn't be doing these checks, period. Suffice it to say, this is unwieldy and unmanageable.

Now you need the same matrix of all actors, just describing whether they are close enough to compare or not. The line-of-sight pass that needs to be run at least every tick takes an^2 resources, and that cannot be reduced by culling, because it IS the culling. It is also overhead unrelated to the stealth mechanics.

THEN you have to identify who can hide from whom: You now have to, for every character, determine if they are trying to hide (read and compare, n time), if they are, determine who needs to try to perceive them (one read, n compares), for each person who is close enough to interact, determine if they can be see the stealthy person (201 more reads, 200 compares, 200 writes, assuming your figure for 200 people in sight at once, still m^2 time, where m is the number of actors interacting after culling). Scaling (not counting culling time) is n+m^2, where n is the number of actors and m is either a waveform or the matrix formed by the product of culling matrix and which actors are trying to hide. And now my math and computer science background is beyond exhausted.

But treating hidden like a status effect that is only broken by events can be done using resources proportional to the number of hidden characters. You can even make collision detection with other actors trigger an event that (conditionally?) ends stealth, such that the character comes out of hiding when someone bumps into them.


DeciusBrutus wrote:
Now you need the same matrix of all actors, just describing whether they are close enough to compare or not. The line-of-sight pass that needs to be run at least every tick takes an^2 resources, and that cannot be reduced by culling, because it IS the culling. It is also overhead unrelated to the stealth mechanics.

This is why you treat each area, or hex, as separate spaces. When you go to pull all local character data, you're only doing so for one hex at a time which would automatically reduce the amount of work by a factor of 256 in the case of PFO, assuming equal distribution of the population. If they don't do this, I would question their sanity.

DeciusBrutus wrote:
THEN you have to identify who can hide from whom: You now have to, for every character, determine if they are trying to hide (read and compare, n time), if they are, determine who needs to try to perceive them (one read, n compares), for each person who is close enough to interact, determine if they can be see the stealthy person (201 more reads, 200 compares, 200 writes, assuming your figure for 200 people in sight at once, still m^2 time, where m is the number of actors interacting after culling). Scaling (not counting culling time) is n+m^2, where n is the number of actors and m is either a waveform or the matrix formed by the product of culling matrix and which actors are trying to hide. And now my math and computer science background is beyond exhausted.

Actually it would be n*(m - 1), with n being stealthed characters and m being the number of characters within line of sight. So, 3 people stealthing among 200 would equal 597 checks since you can't necessarily "see" yourself, which is a pass per stealthed character and is all that's required. Truth be told, though, even in a crowd of 200 people, certain characters will be completely eclipsed by the crowd compared to any position within the crowd so the amount of checks should be less than that. This crowd occlusion effect would be even greater in larger crowds.

DeciusBrutus wrote:
But treating hidden like a status effect that is only broken by events can be done using resources proportional to the number of hidden characters. You can even make collision detection with other actors trigger an event that (conditionally?) ends stealth, such that the character comes out of hiding when someone bumps into them.

A status effect is fine. You would still need to do a check to see whom can see whom unless your advocating the all or nothing approach that Ryan mentioned. Granted, this is simpler, but, it really doesn't take all that much effort computationally and can save overall developer effort while decreasing the networking throughput necessary between the client and server by limiting the amount of data that goes to the client.

Goblin Squad Member

DeciusBrutus wrote:
Buri wrote:
I wouldn't store each and every possible permutation for each character in the game world to each other. The reason being is that all these checks becomes useless at the addition of a single new character or the loss of one which would require each and every check-set to be rebuilt as it would also with two new characters simply coming within line of sight range to each other. You can plan for 4,500 sets to ultimately exist but the climb to that number will get slower and slower and the memory serialization/deserialization for what equates actually to a 10 GB block of RAM will be very, very slow, indeed. It also doesn't make sense for the game to evaluate stealth information for you and I if our characters are at their farthest possible distance from each other which would be at polar opposite ends of the game world. It wouldn't make sense for the game to evaluate this information even if we're in the same town but in different buildings. If we can't see each other, the game shouldn't be doing these checks, period. Suffice it to say, this is unwieldy and unmanageable.

Now you need the same matrix of all actors, just describing whether they are close enough to compare or not. The line-of-sight pass that needs to be run at least every tick takes an^2 resources, and that cannot be reduced by culling, because it IS the culling. It is also overhead unrelated to the stealth mechanics.

THEN you have to identify who can hide from whom: You now have to, for every character, determine if they are trying to hide (read and compare, n time), if they are, determine who needs to try to perceive them (one read, n compares), for each person who is close enough to interact, determine if they can be see the stealthy person (201 more reads, 200 compares, 200 writes, assuming your figure for 200 people in sight at once, still m^2 time, where m is the number of actors interacting after culling). Scaling (not counting culling time) is n+m^2, where n is the number of actors and m is either a waveform or...

I don't know why you think it's neccesary to store a matrix of every characters stealth relationship to every other character in the game, that's completely uneccesary and horribly inefficient. All you need to support are 2 additional values on each character object. A "Stealth" value and a "Spot" value. The server is ALREADY performing a function to determine what objects to send information to a given client about based upon location/distance between the 2. It HAS to do that otherwise clients would be overloaded with incoming data as it is without stealth. So all the server really would have to do is a single logical check when performing that function, see if the "Stealth" value of the character object it's passing to a particular client exceeds that clients Recon value...if so, then just mask the location data for that object when passing to the client. That's the only piece of data thats actualy important to preserving stealth. That way, you are giving the client all the information neccesary to render the stealthed object when it needs to, except the one piece that really allows a hacker to functionaly exploit the situation, the stealthed objects location. Your costs are 2 additional data fields on character objects on the server and a single simple logical comparison check added to a function that the server is ALREADY performing.

That part of the functionality for supporting stealth SHOULDN'T be hard at all. Heck, you don't even really need to treat "stealthed" and "non-stealthed" characters different proceduarly. You just set the "stealth" value for non-stealthed characters to 0 and make sure you never set "Recon" values to less then 1, so non-stealthed characters ALWAYS pass the logic check when being parsed to send to a client.

That part isn't hard.... what's hard is trying to address the fact that when 1 client LEGITIMATELY detects a stealthed object (i.e. has it's location) how you could prevent that client from sending that info to OTHER clients which aren't SUPPOSED to have that information through an out-of-band method. That's something I don't see a practical solution for.

It's why I proposed not worrying about the information disclosure aspect of stealth.... so everyone knows WHERE a stealthed character is....and instead handling the aspect of how a character can MEANINGFULLY interact with a stealthed object. Any time a character meaningfully interacts with an object in the game world, the server ALREADY is performing certain logic checks to determine if the interaction is valid (otherwise you could attack someone from half-way across the world).... so all you are doing is adding one more logic check to those....and since it's only occuring when a character takes an ACTION to meaningfully interact with the object, you aren't processing it on every tick...you are just processing it when an action is taken.... which again the server is already handling. Again, no need to create a matrix of the stealth relationship between every character and every other character...just add 2 fields to the data set returned for each character and do 1 additional logical comparison when they attempt to interact.

Goblin Squad Member

GrumpyMel wrote:

... what's hard is trying to address the fact that when 1 client LEGITIMATELY detects a stealthed object (i.e. has it's location) how you could prevent that client from sending that info to OTHER clients which aren't SUPPOSED to have that information through an out-of-band method...

It's why I proposed not worrying about the information disclosure aspect of stealth.... so everyone knows WHERE a stealthed character is....and instead handling the aspect of how a character can MEANINGFULLY interact with a stealthed object.

You're right, as far as it goes. But often the main value of being stealthed is the fact that no one knows where you are. Without that, stealth is a lot less meaningful. I think this may be at the heart of Ryan's statement:

Ryan Dancey wrote:
Thus, as a game mechanic, its less than ideal and doesn't work the way people wish it would.

Goblinworks Executive Founder

GrumpyMel wrote:
I don't know why you think it's neccesary to store a matrix of every characters stealth relationship to every other character in the game, that's completely uneccesary and horribly inefficient. All you need to support are 2 additional values on each character object. A "Stealth" value and a "Spot" value. The server is ALREADY performing a function to determine what objects to send information to a given client about based upon location/distance between the 2. It HAS to do that otherwise clients would be overloaded with incoming data as it is without stealth. So all the server really would have to do is a single logical check when performing that function, see if the "Stealth" value of the character object it's passing to a particular client exceeds that clients Recon value...if so, then just mask the location data for that object when passing to the client. That's the only piece of data thats actualy important to preserving stealth. That way, you are giving the client all the information neccesary to render the stealthed object when it needs to, except the one piece that really allows a hacker to functionaly exploit the situation, the stealthed objects location. Your costs are 2 additional data fields on character objects on the server and a single simple logical comparison check added to a function that the server is ALREADY performing.

"The server is ALREADY performing a function to determine what objects to send information to a given client about based upon location/distance between the 2."

That's the matrix in question. If we include 4 states (perhaps: distant, near enough to send enough information to cache, near enough to display location, and near but hidden), then the matrix now takes up two bits times the square of the number of characters in the zone. Play with them a while and you can make them non-redundant. Keep in mind that we need to plan for the largest battle of the largest war, and that some NPC actors also need to interact with stealth.

Note that this is bottom-level programming, and would be difficult to implement in a new program and impossible to implement in an existing one.

Goblin Squad Member

Nihimon wrote:
GrumpyMel wrote:

... what's hard is trying to address the fact that when 1 client LEGITIMATELY detects a stealthed object (i.e. has it's location) how you could prevent that client from sending that info to OTHER clients which aren't SUPPOSED to have that information through an out-of-band method...

It's why I proposed not worrying about the information disclosure aspect of stealth.... so everyone knows WHERE a stealthed character is....and instead handling the aspect of how a character can MEANINGFULLY interact with a stealthed object.

You're right, as far as it goes. But often the main value of being stealthed is the fact that no one knows where you are. Without that, stealth is a lot less meaningful. I think this may be at the heart of Ryan's statement:

Ryan Dancey wrote:
Thus, as a game mechanic, its less than ideal and doesn't work the way people wish it would.

Yeah, but personaly I'd rather have something represented in a "less then ideal" fashion then have something not represented AT ALL.

It'd be different if stealth wasn't an important aspect to Pathfinder or D20 STYLE game-play....but it IS very important. In fact, I would argue that it's the CORE function of the Rogue class, at least.

Now, I realize that PFO isn't (and SHOULDN'T) replicate Pathfinder Mechanics.... but I do feel it's important that the mechanics it uses captures the SPIRIT and STYLE of gameplay of that rule-set.

The way I've seen stealth handled in existing MMO's....and I believe you are familiar with this as a LOTRO player... is Stealth = perfect invisability out to a range of 5ft. I believe that's a FAR "less ideal" way of handling it then what I described.

That may be ok for a PVE focused game like LOTRO....but I believe it would make stealthed character VASTLY overpowered for a PvP focused game. That implimentation translates to ANY character with Stealth (i.e. a Level 1 Rogue) is effectively IMMUNE to all Ranged Based attacks (spell or missle weapons).

Goblin Squad Member

Just to insure we do not go off track...and to let newcomers understand the situation, GW has not stated there will not be stealth.

They have stated it would be easiest to implement it all or nothing. So, like in WoW, you can go into stealth, but as soon as one person sees you, you automatically end the "stealth buff". I and a few others have argued for a more meaningful means of stealth, one in which stealth is not a buff, but rather the use of a skill. Those characters who can see you because their perception is higher than your stealth skill should be able to see you, while simultaneously those who should not...cannot. As a low level thief, I should be able to skulk around in a stealthy manner...the fact that everyone can see me is beside the fact that I am trying to be stealthy.

Others might be arguing different points, but most important is that GW has not said there will not be stealth.

Goblinworks Executive Founder

Forencith wrote:

Just to insure we do not go off track...and to let newcomers understand the situation, GW has not stated there will not be stealth.

They have stated it would be easiest to implement it all or nothing. So, like in WoW, you can go into stealth, but as soon as one person sees you, you automatically end the "stealth buff". I and a few others have argued for a more meaningful means of stealth, one in which stealth is not a buff, but rather the use of a skill. Those characters who can see you because their perception is higher than your stealth skill should be able to see you, while simultaneously those who should not...cannot. As a low level thief, I should be able to skulk around in a stealthy manner...the fact that everyone can see me is beside the fact that I am trying to be stealthy.

Others might be arguing different points, but most important is that GW has not said there will not be stealth.

To clarify my position: I hold that the ideal mechanic (everyone sees or doesn't see everyone else independently) is technologically unfeasible, and that development time/money and hardware costs would be better spent if the simpler model were developed and those finite resources were directed at some other aspect of gameplay.


1 person marked this as a favorite.

Decius, I do apologize if I come across as marginalizing your view but I find the concept that a set of calculations that takes 0.000002 seconds which occupies roughly 5 KB of memory for my 200 character example to be technologically unfeasible to be pretty laughable in the age of multi-gigahertz, multi-core processors and memory capacities in the dozens of gigabytes. My phone can handle that processing load easily with its 1.4 GHz processor and 16 GB flash RAM. If it takes the donation of my phone to give PFO the stealth mechanic being discussed in this thread, then so be it. I'll gladly part with it so the game can have it and everyone can enjoy it.

101 to 150 of 187 << first < prev | 1 | 2 | 3 | 4 | next > last >>
Community / Forums / Paizo / Licensed Products / Digital Games / Pathfinder Online / Curious: Stealth Mechanics All Messageboards

Want to post a reply? Sign in.