diff --git a/src/common/utility/i_time.cpp b/src/common/utility/i_time.cpp
index a4177e191c..023860d81a 100644
--- a/src/common/utility/i_time.cpp
+++ b/src/common/utility/i_time.cpp
@@ -46,14 +46,15 @@
 static uint64_t FirstFrameStartTime;
 static uint64_t CurrentFrameStartTime;
 static uint64_t FreezeTime;
-int GameTicRate;
+int GameTicRate = 35;	// make sure it is not 0, even if the client doesn't set it.
 
 double TimeScale = 1.0;
 
 static uint64_t GetClockTimeNS()
 {
 	using namespace std::chrono;
-	return (uint64_t)((duration_cast<microseconds>(steady_clock::now().time_since_epoch()).count()) * (uint64_t)(TimeScale * 1000));
+	if (TimeScale == 1.0) return (uint64_t)(duration_cast<nanoseconds>(steady_clock::now().time_since_epoch()).count());
+	else return (uint64_t)((duration_cast<microseconds>(steady_clock::now().time_since_epoch()).count()) * (uint64_t)(TimeScale * 1000));
 }
 
 static uint64_t MSToNS(unsigned int ms)
@@ -71,10 +72,18 @@ static int NSToTic(uint64_t ns)
 	return static_cast<int>(ns * GameTicRate / 1'000'000'000);
 }
 
+static int NSToBuildTic(uint64_t ns)
+{
+	return static_cast<int>(ns * 120 / 1'000'000'000);
+}
 static uint64_t TicToNS(int tic)
 {
 	return static_cast<uint64_t>(tic) * 1'000'000'000 / GameTicRate;
 }
+static uint64_t BuildTicToNS(int tic)
+{
+	return static_cast<uint64_t>(tic) * 1'000'000'000 / 120;
+}
 
 void I_SetFrameTime()
 {
@@ -142,16 +151,31 @@ uint64_t I_msTime()
 	return NSToMS(I_nsTime());
 }
 
+double I_msTimeF(void)
+{
+	return I_nsTime() / 1'000'000.;
+}
+
 uint64_t I_msTimeFS() // from "start"
 {
 	return (FirstFrameStartTime == 0) ? 0 : NSToMS(I_nsTime() - FirstFrameStartTime);
 }
 
+uint64_t I_GetTimeNS()
+{
+	return CurrentFrameStartTime - FirstFrameStartTime;
+}
+
 int I_GetTime()
 {
 	return NSToTic(CurrentFrameStartTime - FirstFrameStartTime);
 }
 
+int I_GetBuildTime()
+{
+	return NSToBuildTic(CurrentFrameStartTime - FirstFrameStartTime);
+}
+
 double I_GetTimeFrac()
 {
 	int currentTic = NSToTic(CurrentFrameStartTime - FirstFrameStartTime);
@@ -161,6 +185,15 @@ double I_GetTimeFrac()
 	return (CurrentFrameStartTime - ticStartTime) / (double)(ticNextTime - ticStartTime);
 }
 
+double I_GetBuildTimeFrac()
+{
+	int currentTic = NSToBuildTic(CurrentFrameStartTime - FirstFrameStartTime);
+	uint64_t ticStartTime = FirstFrameStartTime + BuildTicToNS(currentTic);
+	uint64_t ticNextTime = FirstFrameStartTime + BuildTicToNS(currentTic + 1);
+
+	return (CurrentFrameStartTime - ticStartTime) / (double)(ticNextTime - ticStartTime);
+}
+
 void I_FreezeTime(bool frozen)
 {
 	if (frozen)
diff --git a/src/common/utility/i_time.h b/src/common/utility/i_time.h
index 47d726de7b..6f95795dd6 100644
--- a/src/common/utility/i_time.h
+++ b/src/common/utility/i_time.h
@@ -10,8 +10,14 @@ void I_SetFrameTime();
 
 // Called by D_DoomLoop, returns current time in tics.
 int I_GetTime();
+// same, but using nanoseconds
+uint64_t I_GetTimeNS();
+
+// Called by Build games in lieu of totalclock, returns current time in tics at ticrate of 120.
+int I_GetBuildTime();
 
 double I_GetTimeFrac();
+double I_GetBuildTimeFrac();
 
 // like I_GetTime, except it waits for a new tic before returning
 int I_WaitForTic(int);
@@ -25,6 +31,9 @@ void I_FreezeTime(bool frozen);
 // [RH] Returns millisecond-accurate time
 uint64_t I_msTime();
 
+// [RH] Returns nanosecond-accurate time in milliseconds
+double I_msTimeF(void);
+
 // [SP] Returns millisecond-accurate time from start
 uint64_t I_msTimeFS();