วิธีซิงค์นาฬิกาผ่านเครือข่ายเพื่อการพัฒนาเกม


10

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

ดังนั้นฉันมีสองคำถาม: 1) ฉันจะซิงค์นาฬิกาของเกมตั้งแต่เริ่มต้นอย่างไรเนื่องจากเครือข่ายล่าช้า 2) ไม่เป็นไรหรือไม่ที่จะซิงค์พวกเขาและเพียงสมมติว่าพวกเขาเหมือนกัน (รหัสของฉันคือเขตเวลาอิสระ) ไม่ใช่เกมที่มีการแข่งขันสูงที่ผู้คนจะเปลี่ยนนาฬิกาเป็นโกง แต่ก็ยัง

เกมกำลังเขียนโปรแกรมใน Java และ Python (การพัฒนาแบบขนานเป็นโครงการ)


7
เงยหน้าขึ้นntp
ratchet freak

5
ฉันแน่ใจว่าคำถามนี้ครอบคลุมอยู่แล้วใน gamedev.stackexchange.com หรืออย่างน้อยก็มี
congusbongus

1
@CongXu: ฉันไม่แน่ใจ ในขณะที่นี่คืองานที่ทำสำหรับเกม แต่ก็ไม่ได้มีความเฉพาะเจาะจงสำหรับการพัฒนาเกม ระบบกระจายอื่น ๆ จำนวนมากต้องการนาฬิกาที่ตรงกัน
Jan Hudec

2
คุณได้ลองใช้ตัวเลือก IPv4 TIMESTAMP แล้วหรือยัง ข้อมูลเพิ่มเติมอยู่ที่linux.die.net/man/7/socketจากที่นั่นไปที่linux.die.net/man/3/cmsgจากนั้นโปรแกรมตัวอย่างเล็ก ๆ ที่pdbuchan.com/rawsock/rawsock.htmlอันสุดท้ายก่อน ส่วน IPv6
ott--

2
เกม (แอปพลิเคชั่น) ไม่ควรสัมผัสนาฬิกาของระบบ
Reinstate Monica - M. Schröder

คำตอบ:


9

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

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

แนวคิดพื้นฐานอาจเกิดจากแต่ละแพ็กเก็ตที่คุณส่งการประทับเวลาคุณส่งและหมายเลขลำดับของและการประทับเวลาเมื่อคุณได้รับแพ็กเก็ตล่าสุดจากอีกด้านหนึ่ง จากนี้คุณคำนวณไปกลับ: ตัวอย่างเช่นถ้าแพ็คเก็ต Q บอกว่ามันถูกส่งที่ 1,000 และได้รับแพ็กเก็ต P 500 ที่มากกว่าที่คุณส่งแพ็กเก็ต P ที่ 0 และได้รับ Q ที่ 800 การเดินทางไปกลับคือ (800 - 0 ) - (1,000 - 500) = 300. ไม่มีวิธีรู้ assymetry ดังนั้นคุณแค่คิดว่าแพ็คเก็ตนั้นใช้เวลาครึ่งหนึ่ง (150) เห็บในทิศทางใดทิศทางหนึ่ง และการประทับเวลาระยะไกลนั้นอยู่ข้างหน้าของท้องถิ่น 1,000 - (800 - 150) = 350 เห็บ การเดินทางไปกลับจะแตกต่างกัน หากคุณคิดว่านาฬิกามีความแม่นยำพอสมควรคุณควรใช้ความสัมพันธ์เฉลี่ยในระยะยาว

โปรดทราบว่าคุณไม่ต้องการใช้นาฬิการะบบสำหรับนาฬิกาเช่นกัน พวกเขาอาจได้รับการซิงโครไนซ์ตรงกลางทำให้คุณหลุด คุณควรใช้clock(CLOCK_MONOTONIC)กับ Unix หรือGetTickCountWindows (ไม่แน่ใจว่า API เหล่านั้นถูกห่อใน Java หรือ Python ในขณะนี้)


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


1

คุณจะรู้ได้อย่างไรว่านาฬิกาของผู้เล่นคนใดถูกต้อง? คุณไม่เพื่อใช้นาฬิกาอ้างอิง

NTP อยู่ที่นี่มากเกินไป - คุณต้องการความแม่นยำเพียง 1 วินาทีเท่านั้นดังนั้นให้ใช้rdateหรือเลือกบางอย่างจากหนึ่งในอัลกอริทึมการซิงโครไนซ์นาฬิกา

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


1

ฉันงบที่สองที่คุณไม่ควรใช้ NTP หรือเข้าใกล้เวลาของระบบสำหรับสิ่งนี้ หากคุณตรวจสอบข้อกำหนดนี้จริงๆคุณจะพบว่าคุณมีความต้องการดังต่อไปนี้:

  • เวลาผ่านไปนานเท่าไหร่จากเฟรมก่อนหน้าเพื่อให้คุณสามารถเลื่อนสิ่งตามเวลาได้ สิ่งนี้อาจแตกต่างกันในไคลเอนต์และเซิร์ฟเวอร์หากทั้งคู่ทำเครื่องหมายในอัตราที่แตกต่างกัน (เช่นบางครั้งคุณอาจเห็นเซิร์ฟเวอร์ที่ทำงานด้วยความเร็วคงที่ 20Hz (หรืออะไรก็ตาม) แต่ลูกค้าที่ได้รับอนุญาตให้ทำงานเร็วที่สุดเท่าที่ต้องการ
  • เวลาที่ผ่านไปนับตั้งแต่แผนที่ปัจจุบันเริ่มต้นขึ้น นี่เป็นตัวจับเวลาแบบ zero-based (เพิ่งเริ่มต้นที่ 0 ทั้งบนไคลเอนต์และเซิร์ฟเวอร์เมื่อพวกเขาเริ่มต้น) และเวลาเซิร์ฟเวอร์ถือเป็น "หลัก" ดังนั้นเซิร์ฟเวอร์ส่งมุมมองปัจจุบันของเวลานี้ไปยังไคลเอนต์สำหรับแต่ละเฟรม ที่ทำงานบนเซิร์ฟเวอร์ สิ่งนี้สามารถทำได้โดยการใช้เวลาเซิร์ฟเวอร์ปัจจุบันและลบเวลาที่เซิร์ฟเวอร์เริ่มทำงานหรือโดยการสะสมเวลาต่อเฟรมข้างต้น นอกจากนี้ยังสามารถใช้เวลาฝั่งไคลเอ็นต์ต่อเฟรมด้านบนเพื่อสร้างจุดการแก้ไขระหว่างเฟรมเซิร์ฟเวอร์สองตัวต่อเนื่องกันได้
  • การอ้างอิง "เวลาพื้นฐาน" ซึ่งเป็นช่วงเวลาอื่นทั้งหมด สิ่งนี้สามารถ (แต่ไม่จำเป็นต้องเป็นอย่างแน่นอนสำหรับเกมทุกประเภท) เหมือนกันทั้งบนไคลเอนต์และเซิร์ฟเวอร์และเริ่มต้นได้เมื่อเริ่มเกม (หรือแผนที่ปัจจุบัน) แต่ไม่เคยเปลี่ยนแปลงหลังจาก (ยกเว้นเกมใหม่ - หรือแผนที่ใหม่ - เริ่มแล้ว)

นี่เป็นโครงร่างที่เรียบง่าย - ฉันไม่พยายามที่จะจัดการกับปัญหาต่างๆเช่น latency / แพ็กเก็ตที่ถูกทิ้ง / etc - แต่มันเป็นเฟรมเวิร์กพื้นฐานที่ควรใช้ทั้งหมด

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

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