|
|
This thread is locked; no one can reply to it.
|
1
2
|
| Server - client question |
|
David Sopala
Member #5,056
September 2004
|
Ok so to make a game run smoothly there needs to be latency right? As in the server waits two ticks to actually do command x or something is this the right way of thinking about it? Also what is the best way to make characters "see" each other without sending the position of everyone on the server? It would seem that running a function to generate a list of people to tell a specific person hey these people are nearby would be costly in terms of time right and since you can't be gauranteed that say X can see Y and Z you can't say Y can see Z since they might be out of range of each other. I am thinking a smaller server 255 or less people could be done with allocating a byte for each player like a quick update, but this structure won't work with larger amounts of people. What advice can you all give me to make a server that won't lagg horribly with just a couple people logged in and make for a good gameplay? What I have right now is a server that will accept incoming connections and allow players to create and login. Working on coding shoot and pushing bullets/projectiles to a stack. The server is written as follows I realize it may not be perfect and inefficent in some places: Server.h 1#ifndef SERVER_H
2#define SERVER_H
3
4#include "mysocket.h"
5#include "serverdatatypes.h"
6#include "string"
7
8using namespace std;
9
10
11class Server
12{
13 public:
14 Server();
15 Server(int MAX_CON);
16 Server(int MAX_CON,int COMMAND_BUFFER); //Max number of connections the server will handle before rejecting incoming
17 //connections and how many commands it will buffer before sending CMD_FAIL back to the client
18 void Initalize(short); //Calls mysocket::startupand starts listening on a socket for incoming connections.
19 bool Push_Command(My_Command *str);
20 My_Command* Get_Command(); //Shall return a string containing a command or NULL if there are no commands to execute.
21 void Die(); //Sends signal to start killing threads and closing connections
22 bool Dead(); //Are all connections closed, threads terminated, and ready to release memory?
23 //STRING BASED SENDING
24 void SendToAll(string); //Sends to everyone on the server announcements and update data.
25 bool SendToPlayer(int, string); //Sends to a certain player. Returns true for sent false for error.
26 void SendToList(int,int *,string); //Sends to a list only can be a group or a zone.
27 //CHAR * BASED SENDING - UPDATE PACKETS ETC
28 void c_SendToAll(char *); //Sends to everyone on the server announcements and update data.
29 bool c_SendToPlayer(int, char *); //Sends to a certain player. Returns true for sent false for error.
30 void c_SendToList(int,int *,char *); //Sends to a list only can be a group or a zone.
31 ~Server();
32 static void Check_Connections(void *); //Checks connections for data pushes commands onto the stack to be executed later.
33 static void Listen(void *);
34
35 private:
36 short Port;
37 bool TimeToDie;
38 bool oktodie[2];
39 int MAX_CONNECTIONS;
40 int MAX_COMMANDS;
41 mysocket Listener;
42 mysocket** Connections;
43 My_Command** Command_List;
44 int* CONNECT_TIMES;
45
46};
47#endif
Server.cpp 1#include "server.h"
2#include "serverdatatypes.h"
3#include <iostream>
4#include <winsock2.h>
5#include <windows.h>
6#include <process.h>
7#include <time.h>
8
9
10
11using namespace std;
12 Server::Server()
13{
14 MAX_CONNECTIONS = 50;
15 MAX_COMMANDS = 250;
16 Connections = new mysocket*[MAX_CONNECTIONS];
17 Command_List = new My_Command*[MAX_COMMANDS];
18 CONNECT_TIMES = new int[MAX_CONNECTIONS];
19 for(int f = 0; f < MAX_CONNECTIONS; f++)
20 {
21 Connections[f] = NULL;
22 }
23 for(int ff = 0;ff < MAX_COMMANDS;ff++)
24 {
25 Command_List[ff] = NULL;
26 }
27 cout<<"Server created with "<<MAX_CONNECTIONS<<" connections and "<<MAX_COMMANDS<<" commands"<<endl<<endl;
28}
29
30
31 Server::Server(int MAX_CON)
32{
33 MAX_CONNECTIONS = MAX_CON;
34 MAX_COMMANDS = MAX_CON*5;
35 Connections = new mysocket*[MAX_CONNECTIONS];
36 Command_List = new My_Command*[MAX_CONNECTIONS*5];
37 CONNECT_TIMES = new int[MAX_CONNECTIONS];
38 for(int f = 0; f < MAX_CONNECTIONS; f++)
39 {
40 Connections[f] = NULL;
41 }
42 for(int ff = 0;ff < MAX_COMMANDS;ff++)
43 {
44 Command_List[ff] = NULL;
45 }
46 cout<<"Server created with "<<MAX_CONNECTIONS<<" connections and "<<MAX_COMMANDS<<" commands"<<endl<<endl;
47}
48
49//Max number of connections the server will handle before rejecting incoming
50//connections and how many commands it will buffer before sending CMD_FAIL back to the client
51 Server::Server(int MAX_CON,int COMMAND_BUFFER)
52{
53 MAX_CONNECTIONS = MAX_CON;
54 MAX_COMMANDS = COMMAND_BUFFER;
55 Connections = new mysocket*[MAX_CONNECTIONS];
56 Command_List = new My_Command*[MAX_COMMANDS];
57 CONNECT_TIMES = new int[MAX_CONNECTIONS];
58 for(int f = 0; f < MAX_CONNECTIONS; f++)
59 {
60 Connections[f] = NULL;
61 }
62 for(int ff = 0;ff < MAX_COMMANDS;ff++)
63 {
64 Command_List[ff] = NULL;
65 }
66 cout<<"Server created with "<<MAX_CONNECTIONS<<" connections and "<<MAX_COMMANDS<<" commands"<<endl<<endl;
67}
68
69void Server::Initalize(short PORT) //Calls mysocket::startup() and starts listening on a socket for incoming connections.
70{
71 Port = PORT;
72 Listener.Startup();
73 _beginthread(Listen,0,(void *)this);
74 _beginthread(Check_Connections,0,(void *)this);
75}
76
77void Server::Listen(void *ptr)
78{
79 Server *this_class = (Server*)ptr;
80 this_class->TimeToDie = false;
81 this_class->oktodie[0] = false;
82 this_class->oktodie[1] = false;
83 this_class->Listener.Listen(this_class->Port); //Start listening on listener socket on Port.
84 cout<<"Now listening for incoming connections on port: "<<this_class->Port<<endl;
85 while(!this_class->TimeToDie)
86 {
87 if(this_class->Listener.HasData())
88 {
89 for(int z = 0;z < this_class->MAX_CONNECTIONS;z++)
90 {
91 if(this_class->Connections[z] == NULL)
92 {
93 cout<<"Accepting new connection on identifier: "<<z<<endl;
94 this_class->Connections[z] = new mysocket(this_class->Listener.Accept());
95 this_class->CONNECT_TIMES[z] = clock();
96 break;
97 }
98 }
99 }
100 }
101 this_class->oktodie[0] = true;
102 _endthread();
103}
104
105void Server::Check_Connections(void *ptr) //Checks connections for data pushes commands onto the stack to be executed later.
106{
107 Server *this_class = (Server*)ptr;
108 this_class->TimeToDie = false;
109 this_class->oktodie[1] = false;
110 while(!this_class->TimeToDie)
111 {
112 for(int z = 0;z < this_class->MAX_CONNECTIONS;z++)
113 {
114 if(this_class->Connections[z] && clock() - this_class->CONNECT_TIMES[z] >= 600000)
115 {
116 delete this_class->Connections[z];
117 this_class->Connections[z] = 0;
118 cout<<"Dropping connection on socket "<<z<<" due to inactivity."<<endl;
119 }
120 if(this_class->Connections[z] && this_class->Connections[z]->HasData())
121 {
122 My_Command *Pushable_Command;
123 Pushable_Command = new My_Command;
124 Pushable_Command->identifier = z;
125 char *data = this_class->Connections[z]->Recv();
126 Pushable_Command->Command = data;
127 if(data && (strcmp(data,"DISCONNECT") != 0))
128 {
129 do{}
130 while(!this_class->Push_Command(Pushable_Command));
131 }
132 else {delete this_class->Connections[z];this_class->Connections[z] = 0;} //If we get nothing back from a read something is probably wrong cull that connection.
133 }
134 }
135 }
136 this_class->oktodie[1] = true;
137 _endthread();
138}
139bool Server::Push_Command(My_Command *cmd)
140{
141 for(int z = 0;z < MAX_COMMANDS;z++)
142 {
143 if(Command_List[z] == NULL)
144 {
145 Command_List[z] = cmd;
146 Command_List[z]->LOCKED = false;
147
148 return true; //Queued up successfully
149 }
150 }
151 return false; //Failed to queue up command queue is full.
152}
153
154My_Command* Server::Get_Command() //Shall return a string containing a command or NULL if there are no commands to execute.
155{
156 for(int z = 0;z < MAX_COMMANDS;z++)
157 {
158 if(Command_List[z] != NULL && !Command_List[z]->LOCKED)
159 {
160 Command_List[z]->LOCKED = true;
161 My_Command *thecommand;
162 thecommand = new My_Command;
163 thecommand->identifier = Command_List[z]->identifier;
164 thecommand->Command = new char[strlen(Command_List[z]->Command)+1];
165 strcpy(thecommand->Command,Command_List[z]->Command);
166 delete Command_List[z];
167 Command_List[z] = NULL;
168 return thecommand;
169 }
170 }
171 return NULL;
172}
173bool Server::Dead()
174{
175 if(oktodie[0] == true && oktodie[1] == true)
176 return true;
177 else return false;
178}
179void Server::Die()
180{
181 TimeToDie = true;
182}
183void Server::SendToAll(string data) //Sends to everyone on the server announcements and update data.
184{
185 for(int x=0;x<MAX_CONNECTIONS;x++)
186 {
187 if(Connections[x])Connections[x]->Send(data.c_str());
188 }
189}
190bool Server::SendToPlayer(int x,string data) //Sends to a certain player. Returns true for sent false for error.
191{
192 if(Connections[x])return Connections[x]->Send(data.c_str());
193 else return false;
194}
195void Server::SendToList(int y,int *x,string data) //Sends to a list only can be a group or a zone.
196{
197 for(int z=0;z<y;z++)
198 {
199 if(Connections[x[z]])Connections[x[z]]->Send(data.c_str());
200 }
201}
202
203void Server::c_SendToAll(char *data) //Sends to everyone on the server announcements and update data.
204{
205 for(int x=0;x<MAX_CONNECTIONS;x++)
206 {
207 if(Connections[x])Connections[x]->Send(data);
208 }
209}
210bool Server::c_SendToPlayer(int x, char *data) //Sends to a certain player. Returns true for sent false for error.
211{
212 if(Connections[x])return Connections[x]->Send(data);
213 else return false;
214}
215void Server::c_SendToList(int y,int *x,char *data) //Sends to a list only can be a group or a zone.
216{
217 for(int z=0;z<y;z++)
218 {
219 if(Connections[x[z]])Connections[x[z]]->Send(data);
220 }
221}
222
223 Server::~Server()
224{
225 int f;
226 for( f = 0;f < MAX_CONNECTIONS;f++)
227 {
228 delete Connections[f];
229 }delete[] Connections;
230 for( f = 0;f < MAX_COMMANDS;f++)
231 {
232 delete Command_List[f];
233 }delete[] Command_List;
234 Listener.Shutdown();
235}
main.cpp 1int main()
2{
3
4 Test_Server = new Server(MAX_CONNECTIONS_TO_SERVER);
5 Test_Server->Initalize(27115);
6 init();
7
8 My_Command* testcommand;
9 while(!kbhit())
10 {
11 do
12 {
13 testcommand = Test_Server->Get_Command();
14 if(testcommand)
15 {
16 cout<<testcommand->Command<<endl;
17 HandleCommand(testcommand);
18 }
19 }
20 while(testcommand);//run all commands in queue
21 }
22 Test_Server->Die();
23 while(!Test_Server->Dead());
24 return 0;
25}
<img src="http://imgur.com/bfHvGkj.jpg" /> |
|
gnolam
Member #2,030
March 2002
|
David Sopala said: Ok so to make a game run smoothly there needs to be latency right? As in the server waits two ticks to actually do command x or something is this the right way of thinking about it? No. Why would you think that? -- |
|
verthex
Member #11,340
September 2009
|
David Sopala said: As in the server waits two ticks to actually do command x or something is this the right way of thinking about it? Your server listens for the client(s) for however many ms you set it. Quote: Also what is the best way to make characters "see" each other without sending the position of everyone on the server? This is possible but only if your simulation is deterministic. You can only take input from your client(s) and send the update from your server with all the positions you changed.
|
|
David Sopala
Member #5,056
September 2004
|
From what I figure- the clients should only send updates when their state changes right? ie 1000xxxx (think up, right, down, left, 4 reserved bits) then lets say they walk up for some amount of time then they stop moving now we get a second packet 0000xxxx and the server stops them moving. Is this a good idea? It seems to have the least amount of traffic in a design that I can think up. The only thing I worry about is the server could potentially get hung up with someone flooding it with commands (repeated presses of a key like up thus sending many update packets) and as you can see from the server design that could cause lagg. Maybe I will need to code a lockout timer 5 commands per 5 seconds or something like that ideas? <img src="http://imgur.com/bfHvGkj.jpg" /> |
|
verthex
Member #11,340
September 2009
|
Another thing you might try doing though I'm not sure what the code looks like for it is synchronizing the clients and the server so that the server expects to receive a message from the clients within some time frame. The clients basically have a timer that is in sync with the server and only send a message within a specific timeframe. The idea would work similarly for the server side as well. The gameloop would execute in sync with the client and the server and the updates ( your controls, scores, etc.) would be sent to your clients and the server sometime after the gameloop.
|
|
David Sopala
Member #5,056
September 2004
|
I added a thread to run updates on the players and such; while the other threads if they find nothing will yeild their timeslice to the update thread to make sure things keep going smoothly(so I hope). Posted before I realized I don't have all the threads posted.... So I have the following threads: Check for incoming connections - yields there is enough time for it to come back only checking one socket in this case so it is quick doesn't need to check twice. Check for data on existing connections - does not yield connections could have data any second now.... Pull commands off the stack and execute them - yields with no commands Update players, NPCs, Monsters, bullets, etc <img src="http://imgur.com/bfHvGkj.jpg" /> |
|
verthex
Member #11,340
September 2009
|
David Sopala said: I added a thread to run updates on the players and such; while the other threads if they find nothing will yeild their timeslice to the update thread to make sure things keep going smoothly(so I hope). Does that mean the server is always listening? If not how do you fix missing packets or are you using udp, thats the norm for games.
|
|
David Sopala
Member #5,056
September 2004
|
You have a thread that will always be checking out on the listener. You also have a thread that will always be checking for incoming commands from the clients/players. TCP actually - The server doesn't "miss" packets although I could put an ACK in there to make sure the client knows the server got the data... The server queues commands up with an identifier and a char* with the command and params. void Server::Initalize(short PORT) //Calls mysocket::startup() and starts listening on a socket for incoming connections. { Port = PORT; Listener.Startup(); _beginthread(Listen,0,(void *)this); _beginthread(Check_Connections,0,(void *)this); } So I start two threads to handle communications and new connections. Now this main thread goes back to main and basicly goes and grabs commands off a stack the server is pushing commands onto. Now lets say people don't send any commands (Test_Server->Get_Command() == NULL) - there is nothing for my guy here to do so he yields that timeslice to someone who needs it. while(!kbhit()) { do { testcommand = Test_Server->Get_Command(); if(testcommand) { HandleCommand(testcommand); } else SwitchToThread(); } while(testcommand); } It also does this while listening on incoming connections for a performance boost I hope. 1void Server::Listen(void *ptr)
2{
3 Server *this_class = (Server*)ptr;
4 this_class->TimeToDie = false;
5 this_class->oktodie[0] = false;
6 this_class->oktodie[1] = false;
7 this_class->Listener.Listen(this_class->Port); //Start listening on listener socket on Port.
8 cout<<"Now listening for incoming connections on port: "<<this_class->Port<<endl;
9 while(!this_class->TimeToDie)
10 {
11 if(this_class->Listener.HasData())
12 {
13 for(int z = 0;z < this_class->MAX_CONNECTIONS;z++)
14 {
15 if(this_class->Connections[z] == NULL)
16 {
17 cout<<"Accepting new connection on identifier: "<<z<<endl;
18 this_class->Connections[z] = new mysocket(this_class->Listener.Accept());
19 this_class->CONNECT_TIMES[z] = clock();
20 break;
21 }
22 }
23 }else SwitchToThread();
24 }
25 this_class->oktodie[0] = true;
26 _endthread();
27}
and if you want to see how I handle commands: 1void HandleCommand(My_Command* cmd)
2{
3 char *str = new char[strlen(cmd->Command)+1];
4 strcpy(str,cmd->Command);
5 char * pch;
6 pch = strtok (str,"_");
7 for(int x = 0;x < NUM_SERVER_FUNCTIONS;x++)
8 {
9 if(pch && strcmp(pch,Functions[x]->Command)==0)
10 {
11 (*Functions[x]->Funct)(cmd);
12 }
13 }
14 delete[] str;
15 delete cmd;
16}
<img src="http://imgur.com/bfHvGkj.jpg" /> |
|
james_lohr
Member #1,947
February 2002
|
David Sopala said: Ok so to make a game run smoothly there needs to be latency right? As in the server waits two ticks to actually do command x or something is this the right way of thinking about it? No. You're not even vaguely on the right track. You haven't even begun to address the real challenge of making a fun and responsive network game which is latency hiding. Have you even established how you are going to maintain consistent state? This will depend a lot on the type of game you are making. For example, for an FPS you'll want the absolute state to be server-side with each client seeing some predictive subset of the state. For an RTS, on the other hand, a good solution is to use a deterministic lock-step engine. Once you've established this, you'll then want to work on decoupling game-play from communication as much as possible so that the game flows smoothly despite latency spikes. Finally comes the hard bit: hiding latency and making the state displayed to the user believable despite the inevitable network latency. This involves interpolation, extrapolation, prediction and reconciliation. Moreover, it requires making the correct game design choices in the first place. You'll see this is all the best online multiplayer games: movement, weapon and gameplay mechanics are chosen to be predictable to within a reasonable degree of accuracy so that reconciliation can be done subtly. It looks to me that you don't really understand the fundamentals. I suggest spending a lot more time thinking about it and reading articles before jumping into client/server design. There are plenty of good articles about like this one: http://www.gamasutra.com/view/feature/3227/designing_fastaction_for_the_.php I would also suggest that you stop dwelling on bandwidth usage. Compared to latency, bandwidth is a non-issue.
|
|
David Sopala
Member #5,056
September 2004
|
Wondered why I kept reading things about 28.8kbps modems... that was written in '97. But you say I am wrong go read that page yourself technically speaking that is what I had said earlier. "•Schedule events in the future if you want them to happen simultaneously for all users. " <img src="http://imgur.com/bfHvGkj.jpg" /> |
|
verthex
Member #11,340
September 2009
|
James Lohr said: For example, for an FPS you'll want the absolute state to be server-side with each client seeing some predictive subset of the state Does that mean my lagging character might shoot someone even if I'm not really pressing the button?
|
|
james_lohr
Member #1,947
February 2002
|
David Sopala said: But you say I am wrong go read that page yourself technically speaking that is what I had said earlier. No, this is not what you said. If this is what you were trying to say then you've either totally misunderstood it, or you've worded it extremely poorly. It is referring to individual events. For example, jumping: This can easily be scheduled a short time in the future whilst still feeling responsive by adding a "crouch" period. So, the user hits the "jump" key, he sees his character start crouching and so it feels to him like there is immediate feedback; however, the message then travels off to the server at which point the server (immediately, or as soon as possible given the network latency!) notifies other clients of the players intention to jump. The actual lift-off only occurs a short (say 300ms) later than the original player hit his jump key. Other players will see the player jump at roughly the same time as the player himself jumps; however, if they look really closely, they might notice that he didn't crouch at all. This is what it is talking about when it says "scheduling events in the future". A few things to note: if you try to employ this technique alone to hide all latency, then the game will start to feel clumsy. So this is just one "trick" in your toolbox of latency hiding techniques. You can't use it everywhere, and so it should not be a mechanic underlying your entire client/server communication engine, or else you will not have the flexibility to use other techniques. verthex said: Does that mean my lagging character might shoot someone even if I'm not really pressing the button? No. Go away.
|
|
GullRaDriel
Member #3,861
September 2003
|
Haha verthex ^^ I hope you were joking thought :-p "Code is like shit - it only smells if it is not yours" |
|
verthex
Member #11,340
September 2009
|
James Lohr said: It is referring to individual events. For example, jumping: This can easily be scheduled a short time in the future whilst still feeling responsive by adding a "crouch" period. So, the user hits the "jump" key, he sees his character start crouching and so it feels to him like there is immediate feedback; however, the message then travels off to the server at which point the server (immediately, or as soon as possible given the network latency!) notifies other clients of the players intention to jump. The actual lift-off only occurs a short (say 300ms) later than the original player hit his jump key. So you're saying the server gets the user input, processes it for physics, etc, and then sends the command for jump to every client including the user to jump. It would solve packet loss (somewhat keeping events in order) but 300ms is a lot of time.
|
|
David Sopala
Member #5,056
September 2004
|
He is still on the 28.8kbps modem mentioned in his other post... 300ms wasn't all that bad back then... [EDIT] I guess by tick he thinks seconds as well. I usually figure a tick to be around 50ms the wait two ticks is indicitive of It will probably be roughly 100ms+-50ms for a response to the client. <img src="http://imgur.com/bfHvGkj.jpg" /> |
|
james_lohr
Member #1,947
February 2002
|
Everything that article says about latency is as applicable now as it was when the article was written, because guess what, the speed of light hasn't gotten any faster, and this is what latency ultimately comes down to. The 300ms example I gave was a specific case where one wishes to guarantee (or at least ensure a high probability) that a particular event will occur at the same time across all clients. ~250 ms is roughly the "worst-case" that any good modern multiplayer game will deal with gracefully, even to this day. As I already said, this is just one technique and is probably not even appropriate for jumping. I just gave this example because I hoped that it would be easy for you to understand as comprehension is evidently not your strong point. I suggest that you give up now, as you're clearly not even intelligent enough to be capable of discerning good advice from bad. You really haven't got a clue what you are doing, and I don't even know why I'm bothering to post in this thread. I guess it was more out of pity because the primary contributor, other than myself, was verthex who is the #1 bullsh*tter on these forums, and so I felt it my duty give some useful information to counter his "noise".
|
|
David Sopala
Member #5,056
September 2004
|
Then please don't post anymore. Actually go post at cprogramming.com they are all like you over there - quick to think they are gods and that no one else has any right to be doing anything. <img src="http://imgur.com/bfHvGkj.jpg" /> |
|
verthex
Member #11,340
September 2009
|
James Lohr said: I suggest that you give up now, as you're clearly not even intelligent enough to be capable of discerning good advice from bad. You really haven't got a clue what you are doing, and I don't even know why I'm bothering to post in this thread. I guess it was more out of pity because the primary contributor, other than myself, was verthex who is the #1 bullsh*tter on these forums, and so I felt it my duty give some useful information to counter his "noise". So you posted a shitty article and now you know something. What experience do you actually have making client server games and if you've never made a FPS please don't reply. Thats what David is trying to make and so am I. Hows this bullshit when everyones talking about it?
|
|
GullRaDriel
Member #3,861
September 2003
|
David, Have you already read the Beej's guide ? On Gamasutra and talking about Network, particularly latency hiding, I loved these articles: Defeating-lag-with-cubic-splines, with these two in addition because they somewhat bring a correction/solution to an error in the article: http://www.gamedev.net/topic/401827-lag-reduction-using-cubic-spline-alias-bezier/ , http://en.wikipedia.org/wiki/Cubic_Hermite_spline "Code is like shit - it only smells if it is not yours" |
|
gnolam
Member #2,030
March 2002
|
David Sopala said: Then please don't post anymore. Translation: Quote: Guys, when I ask for help I actually don't want it. Thanks!
-- |
|
GullRaDriel
Member #3,861
September 2003
|
Meh !
"Code is like shit - it only smells if it is not yours" |
|
Thomas Fjellstrom
Member #476
June 2000
|
I think you're both reaching too high. Start with something simpler. Its pretty much the same as trying to make an MMORPG right out of the gate. You will fail. Probably multiple times. -- |
|
David Sopala
Member #5,056
September 2004
|
Please note I did have a working MMORPG with items enemies and such - it was all tile based wasn't very pretty and the classes weren't written well (that does not mean it didn't work - it worked fine). I see no problem making something simple for fun. You all act like I want to make money or plan to make money with this. It is just a hobby nothing more nothing less. I don't know everything, but judging me saying I'm already going to fail before I even really start to play around. <img src="http://imgur.com/bfHvGkj.jpg" /> |
|
Thomas Fjellstrom
Member #476
June 2000
|
It's just a very common occurrence. A beginner comes along and says "I want to make an MMORPG" or "I want to make a RPG" or "I want to make an FPS", and inevitably, they spend more time fighting with the simple issues like timing and bliting, and give up. Of course if you go into it not expecting to make the "BEST GAME EVER!", and know you're likely to restart several times, that's ok. -- |
|
james_lohr
Member #1,947
February 2002
|
David Sopala said: I don't know everything, but judging me saying I'm already going to fail before I even really start to play around. The trouble is not that I think you're taking on an impossible task nor that you are inherently incapable of succeeding, but rather that you don't seem to have the temperament for it. You came on here asking for advice, but then chose to totally ignore it (and the literature to back it up!) because you took a few of our criticisms personally.
|
|
|
1
2
|