diff --git a/Makefile b/Makefile
index 381a781..814859d 100644
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,7 @@ SDK_BIN_DIR = sdk/redistributable_bin/$(OS)$(ARCH)
LFLAGS = -lhl -lsteam_api -lstdc++ -L $(SDK_BIN_DIR)
SRC = native/cloud.o native/common.o native/controller.o native/friends.o native/gameserver.o \
- native/matchmaking.o native/networking.o native/stats.o native/ugc.o
+ native/matchmaking.o native/networking.o native/stats.o native/ugc.o native/timeline.o
all: ${SRC}
${CC} ${CFLAGS} -shared -o steam.hdll ${SRC} ${LFLAGS}
diff --git a/hlsteam.vcxproj b/hlsteam.vcxproj
index 7672c68..0ee2dc9 100644
--- a/hlsteam.vcxproj
+++ b/hlsteam.vcxproj
@@ -151,6 +151,7 @@
+
diff --git a/native/timeline.cpp b/native/timeline.cpp
new file mode 100644
index 0000000..e0b7aea
--- /dev/null
+++ b/native/timeline.cpp
@@ -0,0 +1,48 @@
+#include "steamwrap.h"
+
+HL_PRIM void HL_NAME(set_timeline_tooltip)( vbyte* description, float timeDelta ) {
+ SteamTimeline()->SetTimelineTooltip((char*)description, timeDelta);
+}
+
+HL_PRIM void HL_NAME(clear_timeline_tooltip)( float timeDelta ) {
+ SteamTimeline()->ClearTimelineTooltip(timeDelta);
+}
+
+HL_PRIM void HL_NAME(set_timeline_game_mode)( ETimelineGameMode mode ) {
+ SteamTimeline()->SetTimelineGameMode(mode);
+}
+
+HL_PRIM TimelineEventHandle_t HL_NAME(add_instantaneous_timeline_event)( vbyte* title, vbyte* description, vbyte* icon, int priority, float startOffsetSeconds, ETimelineEventClipPriority possibleClip ) {
+ return SteamTimeline()->AddInstantaneousTimelineEvent((char*)title, (char*)description, (char*)icon, priority, startOffsetSeconds, possibleClip);
+}
+
+HL_PRIM TimelineEventHandle_t HL_NAME(add_range_timeline_event)( vbyte* title, vbyte* description, vbyte* icon, int priority, float startOffsetSeconds, float durationSeconds, ETimelineEventClipPriority possibleClip ) {
+ return SteamTimeline()->AddRangeTimelineEvent((char*)title, (char*)description, (char*)icon, priority, startOffsetSeconds, durationSeconds, possibleClip);
+}
+
+HL_PRIM TimelineEventHandle_t HL_NAME(start_range_timeline_event)( vbyte* title, vbyte* description, vbyte* icon, int priority, float startOffsetSeconds, ETimelineEventClipPriority possibleClip ) {
+ return SteamTimeline()->StartRangeTimelineEvent((char*)title, (char*)description, (char*)icon, priority, startOffsetSeconds, possibleClip);
+}
+
+HL_PRIM void HL_NAME(update_range_timeline_event)( TimelineEventHandle_t event, vbyte* title, vbyte* description, vbyte* icon, int priority, ETimelineEventClipPriority possibleClip ) {
+ SteamTimeline()->UpdateRangeTimelineEvent(event, (char*)title, (char*)description, (char*)icon, priority, possibleClip);
+}
+
+HL_PRIM void HL_NAME(end_range_timeline_event)( TimelineEventHandle_t event, float endOffsetSeconds ) {
+ SteamTimeline()->EndRangeTimelineEvent(event, endOffsetSeconds);
+}
+
+HL_PRIM void HL_NAME(remove_timeline_event)( TimelineEventHandle_t event ) {
+ SteamTimeline()->RemoveTimelineEvent(event);
+}
+
+#define _TIMELINEEVENT _I64
+DEFINE_PRIM(_VOID, set_timeline_tooltip, _BYTES _F32);
+DEFINE_PRIM(_VOID, clear_timeline_tooltip, _F32);
+DEFINE_PRIM(_VOID, set_timeline_game_mode, _I32);
+DEFINE_PRIM(_TIMELINEEVENT, add_instantaneous_timeline_event, _BYTES _BYTES _BYTES _I32 _F32 _I32);
+DEFINE_PRIM(_TIMELINEEVENT, add_range_timeline_event, _BYTES _BYTES _BYTES _I32 _F32 _F32 _I32);
+DEFINE_PRIM(_TIMELINEEVENT, start_range_timeline_event, _BYTES _BYTES _BYTES _I32 _F32 _I32);
+DEFINE_PRIM(_VOID, update_range_timeline_event, _TIMELINEEVENT _BYTES _BYTES _BYTES _I32 _I32);
+DEFINE_PRIM(_VOID, end_range_timeline_event, _TIMELINEEVENT _F32);
+DEFINE_PRIM(_VOID, remove_timeline_event, _TIMELINEEVENT);
diff --git a/steam/Timeline.hx b/steam/Timeline.hx
new file mode 100644
index 0000000..a4038c3
--- /dev/null
+++ b/steam/Timeline.hx
@@ -0,0 +1,72 @@
+package steam;
+
+enum abstract ETimelineGameMode(Int) from Int to Int {
+ var Invalid = 0;
+ var Playing = 1;
+ var Staging = 2;
+ var Menus = 3;
+ var LoadingScreen = 4;
+}
+
+enum abstract ETimelineEventClipPriority(Int) from Int to Int {
+ var Invalid = 0;
+ var None = 1;
+ var Standard = 2;
+ var Featured = 3;
+}
+
+typedef TimelineEventHandle = hl.I64;
+
+@:access(steam.TimelineNative)
+class Timeline {
+ public static function setTimelineTooltip(description:String, timeDelta:Float):Void {
+ @:privateAccess TimelineNative.setTimelineTooltip(description.toUtf8(), timeDelta);
+ }
+
+ public static function clearTimelineTooltip(timeDelta:Float):Void {
+ TimelineNative.clearTimelineTooltip(timeDelta);
+ }
+
+ public static function setTimelineGameMode(mode:ETimelineGameMode):Void {
+ TimelineNative.setTimelineGameMode(mode);
+ }
+
+ public static function addInstantaneousTimelineEvent(title:String, description:String, icon:String, priority:Int, startOffsetSeconds:Float, possibleClip:ETimelineEventClipPriority = None):TimelineEventHandle {
+ return @:privateAccess TimelineNative.addInstantaneousTimelineEvent(title.toUtf8(), description.toUtf8(), icon.toUtf8(), priority, startOffsetSeconds, possibleClip);
+ }
+
+ public static function addRangeTimelineEvent(title:String, description:String, icon:String, priority:Int, startOffsetSeconds:Float, durationSeconds:Float, possibleClip:ETimelineEventClipPriority = None):TimelineEventHandle {
+ return @:privateAccess TimelineNative.addRangeTimelineEvent(title.toUtf8(), description.toUtf8(), icon.toUtf8(), priority, startOffsetSeconds, durationSeconds, possibleClip);
+ }
+
+ public static function startRangeTimelineEvent(title:String, description:String, icon:String, priority:Int, startOffsetSeconds:Float, possibleClip:ETimelineEventClipPriority = None):TimelineEventHandle {
+ return @:privateAccess TimelineNative.startRangeTimelineEvent(title.toUtf8(), description.toUtf8(), icon.toUtf8(), priority, startOffsetSeconds, possibleClip);
+ }
+
+ public static function updateRangeTimelineEvent(event:TimelineEventHandle, title:String, description:String, icon:String, priority:Int, possibleClip:ETimelineEventClipPriority = None):Void {
+ @:privateAccess TimelineNative.updateRangeTimelineEvent(event, title.toUtf8(), description.toUtf8(), icon.toUtf8(), priority, possibleClip);
+ }
+
+ public static function endRangeTimelineEvent(event:TimelineEventHandle, endOffsetSeconds:Float):Void {
+ TimelineNative.endRangeTimelineEvent(event, endOffsetSeconds);
+ }
+
+ public static function removeTimelineEvent(event:TimelineEventHandle):Void {
+ TimelineNative.removeTimelineEvent(event);
+ }
+}
+
+@:hlNative("steam")
+class TimelineNative {
+ static function setTimelineTooltip(description:hl.Bytes, timeDelta:hl.F32):Void {}
+ static function clearTimelineTooltip(timeDelta:hl.F32):Void {}
+ static function setTimelineGameMode(mode:Int):Void {}
+
+ static function addInstantaneousTimelineEvent(title:hl.Bytes, description:hl.Bytes, icon:hl.Bytes, priority:Int, startOffsetSeconds:hl.F32, possibleClip:Int):hl.I64 { return 0; }
+ static function addRangeTimelineEvent(title:hl.Bytes, description:hl.Bytes, icon:hl.Bytes, priority:Int, startOffsetSeconds:hl.F32, durationSeconds:hl.F32, possibleClip:Int):hl.I64 { return 0; }
+
+ static function startRangeTimelineEvent(title:hl.Bytes, description:hl.Bytes, icon:hl.Bytes, priority:Int, startOffsetSeconds:hl.F32, possibleClip:Int):hl.I64 { return 0; }
+ static function updateRangeTimelineEvent(event:hl.I64, title:hl.Bytes, description:hl.Bytes, icon:hl.Bytes, priority:Int, possibleClip:Int):Void {}
+ static function endRangeTimelineEvent(event:hl.I64, endOffsetSeconds:hl.F32):Void {}
+ static function removeTimelineEvent(event:hl.I64):Void {}
+}