วิธีทำให้นาฬิกาไคลเอนต์เซิร์ฟเวอร์ซิงค์กันสำหรับเกมเครือข่ายที่มีความแม่นยำเช่น Quake 3


15

ฉันกำลังทำงานกับนักยิงจากบนลงล่าง 2D และพยายามอย่างเต็มที่เพื่อคัดลอกแนวคิดที่ใช้ในเกมบนเครือข่ายเช่น Quake 3

  • ฉันมีเซิร์ฟเวอร์ที่เชื่อถือได้
  • เซิร์ฟเวอร์ส่งสแน็ปช็อตไปยังลูกค้า
  • สแน็ปช็อตมีการประทับเวลาและตำแหน่งเอนทิตี
  • เอนทิตีถูกสอดแทรกระหว่างตำแหน่งสแน็ปช็อตเพื่อให้การเคลื่อนไหวดูราบรื่น
  • ตามความจำเป็นการแก้ไขเอนทิตีเกิดขึ้นเล็กน้อย "ในอดีต" เพื่อให้เรามีสแนปชอตหลายรายการที่จะแก้ไขระหว่าง

ปัญหาที่ฉันเผชิญคือ "การซิงโครไนซ์นาฬิกา"

  • เพื่อความเรียบง่ายเรามาเสแสร้งสักครู่ว่าไม่มีเวลาแฝงเป็นศูนย์เมื่อทำการโอนย้ายแพ็คเก็ตไปและกลับจากเซิร์ฟเวอร์
  • หากนาฬิกาเซิร์ฟเวอร์คือ 60 วินาทีข้างหน้าของนาฬิกาไคลเอนต์การประทับเวลาสแน็ปช็อตจะเป็น 60000ms ล่วงหน้าของการประทับเวลาโลคัลไคลเอ็นต์
  • ดังนั้นสแนปชอตเอนทิตีจะรวบรวมและนั่งประมาณ 60 วินาทีก่อนที่ลูกค้าจะเห็นเอนทิตีที่กำหนดใด ๆ ที่ทำให้การเคลื่อนไหวของเขาเพราะมันใช้เวลานานขนาดนั้นในการติดตามลูกค้า

ฉันจัดการเอาชนะมันได้โดยการคำนวณความแตกต่างระหว่างเซิร์ฟเวอร์และนาฬิกาลูกค้าในแต่ละครั้งที่ได้รับสแนปชอต

// For simplicity, don't worry about latency for now...
client_server_clock_delta = snapshot.server_timestamp - client_timestamp;

เมื่อกำหนดระยะเวลาในการแก้ไขเอนทิตีฉันจะเพิ่มความแตกต่างให้กับเวลาปัจจุบันของลูกค้า อย่างไรก็ตามปัญหานี้ก็คือมันจะทำให้เกิดความสะเพร่าเนื่องจากความแตกต่างระหว่างนาฬิกาทั้งสองจะเปลี่ยนแปลงอย่างฉับพลันเนื่องจากสแน็ปช็อตมาถึงเร็วกว่า / ช้ากว่าคนอื่น

ฉันจะซิงโครไนซ์นาฬิกาอย่างใกล้ชิดได้อย่างไรว่าการหน่วงเวลาที่รับรู้ได้เพียงอย่างเดียวคือการเข้ารหัสแบบยากสำหรับการแก้ไขและสิ่งที่เกิดจากเวลาแฝงของเครือข่ายปกติ

กล่าวอีกนัยหนึ่งฉันจะป้องกันไม่ให้การแก้ไขเริ่มต้นช้าเกินไปหรือเร็วเกินไปเมื่อนาฬิกาถูกซิงโครไนซ์อย่างมีนัยสำคัญโดยไม่มีการกระตุก

แก้ไข: ตามวิกิพีเดีย NTP สามารถใช้เพื่อซิงโครไนซ์นาฬิกาผ่านอินเทอร์เน็ตได้ภายในเวลาไม่กี่มิลลิวินาที อย่างไรก็ตามโพรโทคอลนั้นดูซับซ้อนและอาจใช้เงินมากเกินไปในการเล่นเกม?


มันซับซ้อนแค่ไหน? มันเป็นคำร้องขอและตอบสนองแต่ละครั้งด้วยเวลาประทับของการส่งและการมาถึงจากนั้นคณิตศาสตร์สักหน่อยที่จะได้เดลต้า
เฟืองล้อประหลาด

@ratchetfreak: ตาม ( mine-control.com/zack/timesync/timesync.html ) "น่าเสียดายที่ NTP ซับซ้อนมากและที่สำคัญกว่านั้นช้าไปบรรจบกันในช่วงเวลาที่ถูกต้องซึ่งทำให้ NTP น้อยกว่าอุดมคติสำหรับเครือข่าย การเล่นเกมที่ผู้เล่นคาดหวังว่าเกมจะเริ่มต้นทันที ... "
Joncom

คำตอบ:


10

หลังจากค้นหาไปรอบ ๆ ดูเหมือนว่าการซิงโครไนซ์นาฬิกาของคอมพิวเตอร์ตั้งแต่ 2 เครื่องขึ้นไปไม่ใช่เรื่องง่าย โปรโตคอลเช่น NTP ทำงานได้ดี แต่ช้าและซับซ้อนเกินกว่าที่จะนำไปใช้ในเกมได้ นอกจากนี้ยังใช้ UDP ซึ่งจะไม่ทำงานสำหรับฉันเพราะฉันทำงานกับเว็บซ็อกเก็ตซึ่งไม่รองรับ UDP

ฉันพบวิธีการที่นี่แต่ดูเหมือนง่าย:

มันอ้างว่าประสานนาฬิกากับภายใน 150ms (หรือดีกว่า) ของกันและกัน

ฉันไม่รู้ว่าจะดีพอสำหรับวัตถุประสงค์ของฉันหรือไม่ แต่ฉันไม่สามารถหาทางเลือกที่แม่นยำกว่านี้ได้

นี่คืออัลกอริธึมที่มีให้:

ต้องใช้เทคนิคการซิงโครไนซ์นาฬิกาอย่างง่ายสำหรับเกม ตามหลักแล้วควรมีคุณสมบัติดังต่อไปนี้: ความถูกต้องอย่างสมเหตุสมผล (150ms หรือดีกว่า), การรวมอย่างรวดเร็ว, ง่ายต่อการนำไปใช้, สามารถทำงานบนโปรโตคอลแบบสตรีมเช่น TCP

อัลกอริทึมอย่างง่ายพร้อมคุณสมบัติเหล่านี้มีดังนี้:

  1. ลูกค้าประทับเวลาท้องถิ่นปัจจุบันบนแพ็กเก็ต "คำขอเวลา" และส่งไปยังเซิร์ฟเวอร์
  2. เมื่อได้รับจากเซิร์ฟเวอร์เซิร์ฟเวอร์จะทำการประทับตราเวลาของเซิร์ฟเวอร์และส่งคืน
  3. เมื่อลูกค้าได้รับลูกค้าจะลบเวลาปัจจุบันจากเวลาที่ส่งและหารด้วยสองเพื่อคำนวณเวลาแฝง มันลบเวลาปัจจุบันจากเวลาเซิร์ฟเวอร์เพื่อกำหนดเดลตาเวลาไคลเอนต์ - เซิร์ฟเวอร์และเพิ่มครึ่งเวลาในการรับเดลต้านาฬิกาที่ถูกต้อง (จนถึงตอนนี้ algothim นี้คล้ายกับ SNTP มาก)
  4. ผลลัพธ์แรกควรใช้เพื่ออัปเดตนาฬิกาในทันทีเพราะจะทำให้นาฬิกาท้องถิ่นเป็นอย่างน้อย ballpark ที่ถูกต้อง (อย่างน้อยช่วงเวลาที่เหมาะสม!)
  5. ไคลเอนต์ทำซ้ำขั้นตอนที่ 1 ถึง 3 ห้าหรือมากกว่าครั้งหยุดสองสามวินาทีในแต่ละครั้ง การรับส่งข้อมูลอื่นอาจได้รับอนุญาตในระหว่างกาล แต่ควรย่อให้เล็กสุดเพื่อผลลัพธ์ที่ดีที่สุด
  6. ผลลัพธ์ของการรับแพ็คเก็ตจะถูกสะสมและเรียงลำดับในลำดับความหน่วงต่ำสุดถึงลำดับสูงสุดเวลาแฝง ค่ามัธยฐานแฝงถูกกำหนดโดยการเลือกตัวอย่างจุดกลางจากรายการสั่งซื้อนี้
  7. ตัวอย่างทั้งหมดข้างต้นประมาณ 1 ส่วนเบี่ยงเบนมาตรฐานจากค่ามัธยฐานจะถูกละทิ้งและตัวอย่างที่เหลือจะถูกเฉลี่ยโดยใช้ค่าเฉลี่ยเลขคณิต

ความละเอียดอ่อนเพียงอย่างเดียวของอัลกอริทึมนี้คือแพ็คเก็ตที่สูงกว่าค่าเบี่ยงเบนมาตรฐานหนึ่งค่าเหนือค่ามัธยฐานจะถูกทิ้ง วัตถุประสงค์ของการทำเช่นนี้คือกำจัดแพ็กเก็ตที่ถูกส่งซ้ำโดย TCP เพื่อให้เห็นภาพนี้ลองจินตนาการว่าตัวอย่างของห้าแพ็กเก็ตถูกส่งผ่าน TCP และไม่มีการส่งสัญญาณซ้ำ ในกรณีนี้ฮิสโทแกรมเวลาแฝงจะมีโหมดเดียว (คลัสเตอร์) อยู่กึ่งกลางรอบเวลาแฝงค่ามัธยฐาน ทีนี้ลองนึกดูว่าในการทดลองอีกครั้งหนึ่งแพ็คเก็ตหนึ่งในห้านั้นจะถูกส่งใหม่ การส่งสัญญาณใหม่จะทำให้ตัวอย่างนี้หล่นไปทางขวาบนฮิสโทแกรมเวลาแฝงโดยเฉลี่ยสองครั้งไกลกว่าค่ามัธยฐานของโหมดหลัก เพียงแค่ตัดตัวอย่างทั้งหมดที่ตกมากกว่าหนึ่งส่วนเบี่ยงเบนมาตรฐานจากค่ามัธยฐานโหมดจรจัดเหล่านี้จะถูกกำจัดได้อย่างง่ายดายโดยสมมติว่าพวกเขาไม่ได้ประกอบด้วยสถิติจำนวนมาก

วิธีแก้ปัญหานี้ดูเหมือนจะตอบคำถามของฉันได้อย่างน่าพอใจเพราะมันประสานนาฬิกาและหยุดแล้วทำให้เวลาไหลเป็นเส้นตรง ในขณะที่วิธีการเริ่มต้นของฉันมีการปรับปรุงนาฬิกาอย่างต่อเนื่องทำให้เวลาในการข้ามไปเล็กน้อยเมื่อได้รับสแนปชอต


สิ่งนี้พิสูจน์แล้วว่าทำงานให้คุณได้อย่างไร? ฉันอยู่ในสถานการณ์เดียวกันตอนนี้ ฉันใช้เซิร์ฟเวอร์เฟรมเวิร์กที่รองรับ TCP เท่านั้นดังนั้นฉันไม่สามารถใช้ NTP ซึ่งส่ง UDP ดาตาแกรม ฉันพยายามหาอัลกอริธึมการซิงโครไนซ์เวลาที่อ้างว่าทำการซิงโครไนซ์เวลาที่เชื่อถือได้ผ่าน TCP การซิงโครไนซ์ภายในไม่กี่วินาทีนั้นเพียงพอสำหรับความต้องการของฉัน
dynamokaj

@dynamokaj ทำงานได้ค่อนข้างดี
Joncom

เย็น. เป็นไปได้หรือไม่ที่คุณสามารถแบ่งปันการดำเนินการได้
dynamokaj

@dynamokaj ดูเหมือนว่าฉันไม่สามารถหาการดำเนินการดังกล่าวในโครงการใด ๆ ที่ฉันสามารถคิดได้ในขณะนี้ อีกวิธีหนึ่งที่ใช้งานได้ดีพอสำหรับฉันคือ: 1) ใช้เวลาในการตอบสนองที่คุณคำนวณได้จากการร้องขอการตอบสนองต่อการ ping หนึ่งครั้งจากนั้น 2) สำหรับการตอบสนองในอนาคตทั้งหมดทวีขึ้นเรื่อย ๆ นี่เป็นเอฟเฟ็กต์ "เฉลี่ย" ซึ่งแม่นยำมากสำหรับจุดประสงค์ของฉัน
Joncom

ไม่มีปัญหา. ฉันใช้บริการแบ็คเอนด์ของฉันใน Google App Engine ดังนั้นในโครงสร้างพื้นฐานของ Googles ที่เซิร์ฟเวอร์ถูกซิงโครไนซ์โดยใช้เซิร์ฟเวอร์ Google NTP: time.google.com ( developers.google.com/time ) ดังนั้นฉันจึงใช้ไคลเอนต์ NTP ต่อไปนี้สำหรับลูกค้า Xamarin Mobile ของฉัน เพื่อรับออฟเซตระหว่างไคลเอ็นต์และเซิร์ฟเวอร์ components.xamarin.com/view/rebex-time - ขอบคุณที่สละเวลาตอบ
dynamokaj

1

โดยพื้นฐานแล้วคุณไม่สามารถแก้ไขโลกทั้งโลกได้และในที่สุดคุณจะต้องวาดเส้น

หากเซิร์ฟเวอร์และไคลเอนต์ทั้งหมดใช้อัตราเฟรมเดียวกันพวกเขาเพียงต้องการซิงโครไนซ์เมื่อเชื่อมต่อและบางครั้งหลังจากนั้นโดยเฉพาะหลังจากเหตุการณ์เวลาแฝง ความหน่วงแฝงไม่ส่งผลกระทบต่อการไหลของเวลาหรือความสามารถของพีซีในการวัดดังนั้นในหลายกรณีแทนที่จะต้องสอดแทรกคุณต้องประมาณค่า สิ่งนี้จะสร้างเอฟเฟ็กต์ที่ไม่พึงประสงค์เท่า ๆ กัน แต่มันคือสิ่งที่เป็นและคุณต้องเลือกความชั่วร้ายที่มีน้อยที่สุด

พิจารณาว่าใน MMO ยอดนิยมหลายแห่งผู้เล่นที่มีความล่าช้าจะเห็นได้ชัด หากคุณเห็นพวกเขาทำงานอยู่ในสถานที่โดยตรงลูกค้าของคุณกำลังคาดการณ์ เมื่อลูกค้าของคุณได้รับข้อมูลใหม่ผู้เล่น (บนไคลเอนต์ของพวกเขา) อาจได้ย้ายระยะทางมากและจะ "ยางรัด" หรือเทเลพอร์ตไปยังตำแหน่งใหม่ ("กระตุก" ที่คุณพูดถึง?) สิ่งนี้เกิดขึ้นแม้ในเกมแบรนด์เนมที่มีชื่อเสียง

ในทางเทคนิคนี่เป็นปัญหากับโครงสร้างเครือข่ายของผู้เล่นไม่ใช่เกมของคุณ จุดที่มันเคลื่อนจากจุดหนึ่งไปอีกจุดหนึ่งคือเส้นที่คุณต้องวาด รหัสของคุณในคอมพิวเตอร์ 3 เครื่องแยกกันควรบันทึกเวลาที่ใช้ไปในจำนวนเดียวกันหรือมากกว่า หากคุณไม่ได้รับการอัพเดตข้อมูลดังกล่าวจะไม่ส่งผลกระทบต่ออัตราเฟรม Update () ของคุณ หากมีสิ่งใดควรเร็วกว่าเนื่องจากอาจมีการอัปเดตน้อยกว่า

"ถ้าคุณมี crummy internet คุณจะไม่สามารถเล่นเกมนี้ได้ในเชิงแข่งขัน"
ที่ไม่ผ่านเจ้าชู้หรือบั๊ก

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.