« Trouble Shootings for Modeling | メイン | What's Urban Terror »

April 16, 2005

■ W:ETソースコード朗読会 第六回

Reading the Source Code of W:ET #6

今回のねらい

何だかんだで六回目になりました。ETも久しぶりのバージョンアップを受け、2.60になりました。これに伴い、ソースコードも新しいものが配布されています。今回からはこちらを扱います。ダメージ減衰に関するコード修正その他いろいろフィックスがなされているようです。

今回はアンチラグ機能がどのように働いているのか、についてみていきましょう。

アンチラグって?

アンチラグは何するものぞ、と思われる方がいらっしゃるかもしれません。ラグとはご存知の通り、クライアントサーバー間での通信処理によって生ずる同期のずれ、タイムラグのことです。そのため、クライアント側ではきちんとターゲットに対して命中する方向でも、サーバー上のデータでは既にターゲットはそこにはおらず命中しない現象が起きます。そのため、Pingの大きなサーバーでは「先読み」をして攻撃を行うことがいわばFPSの常識になっていました。

これを解決するためにアンチラグが導入されています。つまり、クライアントの動作内容と行った時間をサーバーが考慮して、クライアントが実際に見ている条件下にて当たり判定を行うものです。このため、クライアントは先読みをする必要はなくなります。

しかしまったく問題がないわけではありません。Pingの小さい人と大きな人が出会い頭にパンツァーファウストを撃ったとします。結果、Pingの小さい人が大きな人を「必ず」先制します。これは通信反応速度の差であり、アンチラグ機能をもってしてもこれを埋めることはできないのです。

アンチラグ機能実現のためのコード群が入っているのはg_antilag.cが主です。Ver2.60になってからナイフとMG42に関してもアンチラグ機能が働くようになりました。つまり、当たり判定の際にはここの関数が呼び出されていることになります。さっそくみていきましょう。

アンチラグ:位置情報保存

void G_StoreClientPosition( gentity_t* ent ) {
	int top, currentTime;
	
	if (!(	ent->inuse && 
 			(ent->client->sess.sessionTeam == TEAM_AXIS || ent->client->sess.sessionTeam == TEAM_ALLIES) && 
 			ent->r.linked &&
 			(ent->health > 0) &&
 			!(ent->client->ps.pm_flags & PMF_LIMBO) &&
			(ent->client->ps.pm_type == PM_NORMAL)
 	)) {
		return;
	}

	(省略)

	ent->client->clientMarkers[top].servertime =	level.time;
	ent->client->clientMarkers[top].time =			currentTime;
}

一番最初の関数です。G_StoreClientPositionという名前からして、クライアントの位置情報を保存しておくもののようですね。詳しく見ていきましょう。

	top = ent->client->topMarker;

	// new frame, mark the old marker's time as the end of the last frame
	if( ent->client->clientMarkers[top].time < level.time) {
		ent->client->clientMarkers[top].time = level.previousTime;
		top = ent->client->topMarker = ent->client->topMarker == MAX_CLIENT_MARKERS - 1 ? 0 : ent->client->topMarker + 1;
	}

topにtopMarkerを代入しています。つまり、topはサーバーに記録されている最新のクライアント位置情報の「番号」です。

次のif条件は、記録されているクライアントの時間がサーバーの時間よりも小さいかどうかをみています。もしそうなら、この記録時間をpreviousTimeに置き換え、topを1個増やす、つまり新しい記録番号にします。


	VectorCopy( ent->r.mins,						ent->client->clientMarkers[top].mins );
	VectorCopy( ent->r.maxs,						ent->client->clientMarkers[top].maxs );
	VectorCopy( ent->r.currentOrigin,				ent->client->clientMarkers[top].origin );
	ent->client->clientMarkers[top].servertime =	level.time;
	ent->client->clientMarkers[top].time =			currentTime;

ここでは新しくクライアントの位置情報と時間を記録しています。

サーバーではクライアントの「位置」と「時間」がセットで記録されていることを憶えておいてくださいね。

アンチラグ:Historical Trace

クライアントの位置情報が時間とともに保存されていることを確認しました。それでは実際の当たり判定を見ていきましょう。これはG_HistoricalTrace関数で行われています。

	if( !g_antilag.integer || !ent->client) {
		G_AttachBodyParts( ent ) ;

		trap_Trace( results, start, mins, maxs, end, passEntityNum, contentmask );

		res = G_SwitchBodyPartEntity(&g_entities[results->entityNum]);
		POSITION_READJUST

		G_DettachBodyParts();
		return;
	}

ここはアンチラグ機能が無効、または対象がクライアントでない場合の処理です。いわゆる普通の当たり判定を行っています。ちなみにG_AttachBodyPartsとG_DettachBodyPartsはクライアントにヘッドや足などの個別の当たり判定箇所をくっつけたり取り除いたりします。

	G_AdjustClientPositions( ent, ent->client->pers.cmd.serverTime, qtrue );

	G_AttachBodyParts( ent ) ;

	trap_Trace( results, start, mins, maxs, end, passEntityNum, contentmask );

	res = G_SwitchBodyPartEntity(&g_entities[results->entityNum]);
	POSITION_READJUST

	G_DettachBodyParts();

そしてこちらがアンチラグ機能が有効な場合の当たり判定です。まずはG_AdjustClientPositionsから見ていきましょう。二つ目の引数に、ent->client->pers.cmd.serverTimeをエントリーしている点を憶えておいてください。この数値は、クライアントが最後に受け取ったデータのタイムスタンプです。つまり、クライアントが今実際に見ている状況を再現するサーバー時間です。

void G_AdjustClientPositions( gentity_t* ent, int time, qboolean forward) {
	int			i;
	gentity_t	*list;

	for ( i = 0; i < level.numConnectedClients; i++, list++) {
		list = g_entities + level.sortedClients[i];
		// Gordon: ok lets test everything under the sun
 		if (	list->client && 
 				list->inuse && 
 				(list->client->sess.sessionTeam == TEAM_AXIS || list->client->sess.sessionTeam == TEAM_ALLIES) && 
 				(list != ent) &&
 				list->r.linked &&
 				(list->health > 0) &&
 				!(list->client->ps.pm_flags & PMF_LIMBO) &&
				(list->client->ps.pm_type == PM_NORMAL)
 		) {
			if(forward) {
				G_AdjustSingleClientPosition( list, time );
			} else {
				G_ReAdjustSingleClientPosition( list );
			}
		}
	}
}

大きなif条件がありますが、まとめると対象がクライアントでチームに参加していて攻撃対象と攻撃者が同じではなくてヘルスが1以上であることをみています。もしそうなら、G_AdjustSingleClientPositionによって攻撃対象(条件を満たすすべてのクライアント)の位置情報を攻撃しているクライアントのtime(ent->client->pers.cmd.serverTime)によって修正します。実際のコードを見ていきましょう。

static void G_AdjustSingleClientPosition( gentity_t* ent, int time ) {
	int i, j;

	if( time > level.time) {
		time = level.time;
	} // no lerping forward....

	i = j = ent->client->topMarker;
	do {
		if(ent->client->clientMarkers[i].time <= time) {
			break;
		}

		j = i;
		i = (i > 0) ? i - 1 : MAX_CLIENT_MARKERS - 1;
	} while(i != ent->client->topMarker);

	if(i == j) { // oops, no valid stored markers
		return;
	}

最初のif文は、クライアントがサーバーよりも先のフレームに達していた場合これをサーバーの時間に同期させるためのものです(こんな状況はローカル接続でもしない限りなかなか起こりえませんが・・・)。巷ではcom_maxfpsを操作することによって当たりやすくなるなどの噂が囁かれています。実際にfps設定値はこのあたりでフレームごとの時間という形で効いてきます。つまり、フレームごとの時間が短いほど位置情報が詳しく記録されるため、当たり判定が細かく計算されます。このため当たり易さに差が生じる可能性があるのです。

次のwhile構文がまさにアンチラグ機能そのものです。修正対象となるクライアントの位置情報が登録されている時間と攻撃者が見ている時間を比較して、同じか前者が後者よりも前になったときの位置登録番号を求めています。

これによって「攻撃者視点から」のタイムラグを打ち消しストレスのないゲームプレイを実現しています。ただし、「被攻撃者」からは自身が攻撃を受けるタイミングではないはずの時にもダメージを被る可能性があります。これは攻撃者のPingが悪ければ悪いほど生じやすくなるので、どこまでこの機能を有効にするかは実に考えどころです。

次に参りましょう。

	if(i != ent->client->topMarker) {
		float frac = ((float)(ent->client->clientMarkers[j].time - time)) / (ent->client->clientMarkers[j].time - ent->client->clientMarkers[i].time);

		LerpPosition(ent->client->clientMarkers[j].origin,		ent->client->clientMarkers[i].origin,	frac,	ent->r.currentOrigin);
		LerpPosition(ent->client->clientMarkers[j].mins,		ent->client->clientMarkers[i].mins,		frac,	ent->r.mins);
		LerpPosition(ent->client->clientMarkers[j].maxs,		ent->client->clientMarkers[i].maxs,		frac,	ent->r.maxs);
	} else {
		VectorCopy( ent->client->clientMarkers[j].origin,		ent->r.currentOrigin );
		VectorCopy( ent->client->clientMarkers[j].mins,			ent->r.mins );
		VectorCopy( ent->client->clientMarkers[j].maxs,			ent->r.maxs );
	}

ここは、フレームとフレームの間の「時間的な」隙間を補完するための部分です。例えば攻撃者が行動した時間が3秒めだっととします。ところがサーバーに保存されているクライアントの位置情報が1秒と5秒のものしかない場合があります。そんな時、3秒目の位置情報を推測するために、直前と直後の位置をクライアントが直線的に動いたと仮定し、攻撃者の行動時間での位置を計算しています。

当然ながらここにも誤差を含む可能性はありますが、大抵の場合は良好な結果となります。こういった部分を見ても、実戦さながらのジグザグな動きが相手の攻撃を回避する上でとても有効であることがわかります。

さて、話をG_HistoricalTraceに戻します。G_AdjustSingleClientPositionによってクライアントの位置を修正もしくは補完し、そしてtrap_Traceにより当たり判定を見ています。この結果はresultsに格納されます。そして過去の状態になっている各クライアントの位置情報を、G_AdjustClientPositionsを3つ目の引数をfalseで呼び出すことで元に戻しています。

ちなみにG_SwitchBodyPartEntityは当たった体の部位に応じて処理を分ける関数のようですが、現在のところ目だった機能は果たしていません。頭でも胴体でも足でも「クライアントに当たった」としています。

これ以降のコードは、位置修正してクライアントにヒットしたとき位置修正なしでの当たり判定を行い、この結果をresに代入しています。

まとめ

今回はアンチラグ機能ということで、サーバーとクライアント間での時間差を考慮するコードを見てきました。なかなか興味深い部分でしたね。クライアント間の環境差は仕方のないことといえばそれまでですが、コードでこれを公平に補うことが出来るなら素晴らしいことだと思います。クライアントが個人を益するために補正をかけることはチート行為です。やめましょうね。

投稿者 ikanatto : April 16, 2005 06:14 PM

■ コメント

Paying one debt at one rate usually trumps eight credit cards at varying rates in the debt game. Both here gold mountingsand there 1ct diamond ring eternity ring [URL=http://search.bbc.co.uk/cgi-bin/search/results.pl?tab=web&q=itemtag+er+031] eternity ring[/URL] http://search.bbc.co.uk/cgi-bin/search/results.pl?tab=web&q=itemtag+er+031 Ground Rent Survivorship Arrears

投稿者 eternity ring : October 16, 2006 12:15 PM

Student loan consolidation can significantly lower your monthly payment by lengthening the term of your loans, with no prepayment penalties. Both here Firm Commitmentand there Bridge Financing Financial Depositor Institutions http://www.vadecom.net/cheap-links/Business-3.html Line of Credit Exclusive Right-to-Sell Estimated Closing Costs Statement

投稿者 Financial Depositor Institutions : October 19, 2006 01:05 PM

Zero percent is the ultimate in great deals and zero-percent cards are great for consolidating debt by transferring balances from other cards into one. old debt http://oldweb.uwp.edu/academic/business/news/articles/old-debt.html Common Areas Estimated Closing Costs Statement Bill of Sale

投稿者 old debt : October 25, 2006 11:27 PM

Combining multiple payments into a single monthly check lowers interest rates and can positively affect your credit score. debt consildation http://oldweb.uwp.edu/academic/business/news/articles/debt-consildation.html Physical Depreciation Agent Commitment

投稿者 debt consildation : October 29, 2006 04:54 AM

Home equity loans are like a financial (addiction) because as your home value goes up, every year you're able to borrow a little more and a little more. mortgage rates bank http://idea.zanestate.edu/wordpress/articles/mortgage-rates-bank.html home mortgage loan rate washington mortgage rates chase manhattan mortgage

投稿者 mortgage rates bank : November 1, 2006 04:30 AM

Debt and debt-consolidation strategies go together in the American economy like peanut butter and jelly. wells fargo mortgage http://theory.lcs.mit.edu/theory-seminars/2005fa/docs/Wells-Fargo-Mortgage.html mortgage loans mortgage rate history what are mortgage points

投稿者 wells fargo mortgage : November 8, 2006 07:54 PM

Americans borrow between $400 billion and $500 billion a year against home equity alone. secured debt http://www.cv.nrao.edu/~abridle/php/gallery/docs/Secured-Debt.html mortgage payment estimates stock options financial mortgage

投稿者 secured debt : November 14, 2006 09:51 PM

Great work!
[url=http://fgzbilwu.com/hknv/jtne.html]My homepage[/url] | [url=http://aasbyvpy.com/nkkb/rmok.html]Cool site[/url]

投稿者 Debbie : November 20, 2006 02:36 PM

Good design!
My homepage | Please visit

投稿者 Gabriel : November 20, 2006 02:36 PM

投稿者 Greg : November 20, 2006 02:36 PM

投稿者 Angie : November 22, 2006 05:34 PM

投稿者 Lionel : November 22, 2006 05:53 PM

投稿者 Jack : November 22, 2006 06:16 PM

投稿者 Emma : November 22, 2006 06:37 PM

投稿者 Frank : November 22, 2006 07:05 PM

投稿者 Caleb : November 23, 2006 07:22 PM

投稿者 Sarah : November 23, 2006 07:50 PM

投稿者 Phillip : November 23, 2006 08:19 PM

投稿者 Rex : November 23, 2006 08:44 PM

投稿者 Abby : November 23, 2006 09:10 PM

投稿者 Olga : November 24, 2006 10:04 PM

投稿者 Jack : November 24, 2006 10:30 PM

投稿者 Barbara : November 24, 2006 10:55 PM

投稿者 Marla : November 24, 2006 11:19 PM

投稿者 Gabriel : November 24, 2006 11:44 PM

投稿者 Julie : November 25, 2006 09:37 PM

投稿者 Sabrina : November 25, 2006 10:04 PM

投稿者 Brad : November 25, 2006 10:29 PM

投稿者 Tina : November 25, 2006 10:56 PM

投稿者 Irene : November 25, 2006 11:23 PM

投稿者 Colin : November 26, 2006 09:26 PM

投稿者 Laura : November 26, 2006 09:55 PM

投稿者 Candice : November 26, 2006 10:28 PM

投稿者 Candice : November 26, 2006 10:28 PM

投稿者 Holly : November 26, 2006 10:54 PM

投稿者 Ingrid : November 26, 2006 11:23 PM

Debt and debt-consolidation strategies go together in the American economy like peanut butter and jelly. non profit debt http://www.physics.ohio-state.edu/asc/docs/Non-Profit-Debt.html

投稿者 non profit debt : November 27, 2006 04:08 PM

投稿者 Candice : November 27, 2006 09:23 PM

投稿者 Keith : November 27, 2006 09:51 PM

投稿者 May : November 27, 2006 10:26 PM

投稿者 Zack : November 27, 2006 11:03 PM

投稿者 Gabriel : November 27, 2006 11:33 PM

投稿者 Jean : November 29, 2006 01:35 AM

投稿者 Jean : November 29, 2006 01:58 AM

投稿者 Fawn : November 29, 2006 02:22 AM

投稿者 Candice : November 29, 2006 02:47 AM

投稿者 Michelle : November 29, 2006 03:10 AM

投稿者 David : November 29, 2006 10:16 PM

投稿者 Ron : November 29, 2006 10:45 PM

投稿者 Karl : November 29, 2006 11:11 PM

投稿者 Gloria : November 29, 2006 11:38 PM

投稿者 Nick : November 30, 2006 12:03 AM

投稿者 Terry : November 30, 2006 12:27 AM

投稿者 Timothy : November 30, 2006 09:03 PM

投稿者 Cindy : November 30, 2006 09:29 PM

投稿者 Jean : November 30, 2006 09:54 PM

投稿者 Maggie : November 30, 2006 10:16 PM

投稿者 Alice : November 30, 2006 10:45 PM