[Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

Post gaming related scripts
Tactical Shrimp
Posts: 31
Joined: 11 Nov 2015, 22:29

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

11 Jul 2016, 19:23

evilC wrote:Yeah... It seems like he omitted a critical line from the code he posted...

This is what he was quoting, but he omitted the Loop statement.

Code: Select all

$F2::
Loop, 3
	;                                    ↓ Left or Right (Negative or Positive)
	DllCall("mouse_event", uint, 1, int, 2, int, 2, uint, 0, int, 0)
	;                                            ↑ Up or Down (Negative or Positive)
return
So I am guessing that 1 mouse_event is not too much, he just issued 3 with no delay between, which is a VERY quick movement.

oh my god i love younthank you sooooo much you just solved everything
austin0751

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

17 Jul 2016, 21:34

Is it possible to make this script hold the wasd keys down while the mouse is moving one direction instead of just rapidly pressing the keys?
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

28 Jul 2016, 14:40

Upped timeout from 10ms to 50ms.
This should help make the stop detection more reliable.

Split OP into Library and sample code sections.

Added second example - a diagnostic tool to show you the delta moves in real-time.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

06 Aug 2016, 12:42

I just had a request from someone about multiple mouse input. I looked at the code for EitherMouse, and it is really rather simple to do. Here is a version of the code which can identify between different mice:

[EDIT] Fixed for x64

Code: Select all

#SingleInstance,Force
 
Gui, Add, ListBox, w300 h200 hwndhOutput
Gui, Add, Text, xm w300 center, Hit F12 to toggle on / off
Gui, Show,, Mouse Watcher
 
MacroOn := 0
new MouseDelta("MouseEvent")
 
return
 
GuiClose:
	ExitApp
 
F12::
	MacroOn := !MacroOn
	return
 
; Gets called when mouse moves or stops
; x and y are DELTA moves (Amount moved since last message), NOT coordinates.
MouseEvent(MouseID, x := 0, y := 0){
	global MacroOn
	global hOutput
	static text := ""
	static LastTime := 0
 
	if (!MacroOn)
		return
 
	t := A_TickCount
	text := (x == 0 && y == 0) ? "STOPPED" : "x: " x ", y: " y (LastTime ? (", Delta Time: " t - LastTime " ms, MouseID: " MouseID) : "")
	GuiControl, , % hOutput, % text
	sendmessage, 0x115, 7, 0,, % "ahk_id " hOutput
	LastTime := t
}
 
; Instantiate this class and pass it a func name or a Function Object
; The specified function will be called with the delta move for the X and Y axes
; Normally, there is no windows message "mouse stopped", so one is simulated.
; After 10ms of no mouse movement, the callback is called with 0 for X and Y
Class MouseDelta {
	__New(callback){
		static DevSize := 8 + A_PtrSize
		static RIDEV_INPUTSINK := 0x00000100
 
		this.TimeoutFn := this.TimeoutFunc.Bind(this)
 
		this.Callback := callback
		; Register mouse for WM_INPUT messages.
		VarSetCapacity(RAWINPUTDEVICE, DevSize)
		NumPut(1, RAWINPUTDEVICE, 0, "UShort")
		NumPut(2, RAWINPUTDEVICE, 2, "UShort")
		NumPut(RIDEV_INPUTSINK, RAWINPUTDEVICE, 4, "Uint")
		; WM_INPUT needs a hwnd to route to, so get the hwnd of the AHK Gui.
		; It doesn't matter if the GUI is showing, it still exists
		Gui +hwndhwnd
		NumPut(hwnd, RAWINPUTDEVICE, 8, "Uint")
 
		this.RAWINPUTDEVICE := RAWINPUTDEVICE
		DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
		fn := this.MouseMoved.Bind(this)
		OnMessage(0x00FF, fn)
	}
 
	__Delete(){
		static RIDEV_REMOVE := 0x00000001
		static DevSize := 8 + A_PtrSize
		RAWINPUTDEVICE := this.RAWINPUTDEVICE
		NumPut(RIDEV_REMOVE, RAWINPUTDEVICE, 4, "Uint")
		DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
	}
 
	; Called when the mouse moved.
	; Messages tend to contain small (+/- 1) movements, and happen frequently (~20ms)
	MouseMoved(wParam, lParam){
		Critical
		; RawInput statics
		static DeviceSize := 2 * A_PtrSize, iSize := 0, sz := 0, offsets := {x: (20+A_PtrSize*2), y: (24+A_PtrSize*2)}, uRawInput
 
		static axes := {x: 1, y: 2}
 
		; Get hDevice from RAWINPUTHEADER to identify which mouse this data came from
		VarSetCapacity(header, pcbSize, 0)
		If (!DllCall("GetRawInputData", "UPtr", lParam, "uint", 0x10000005, "UPtr", &header, "Uint*", pcbSize, "Uint", pcbSize) or ErrorLevel)
			Return 0
		ThisMouse := NumGet(header, 8, "UPtr")

		; Find size of rawinput data - only needs to be run the first time.
		if (!iSize){
			r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", 0, "UInt*", iSize, "UInt", 8 + (A_PtrSize * 2))
			VarSetCapacity(uRawInput, iSize)
		}
		sz := iSize	; param gets overwritten with # of bytes output, so preserve iSize
		; Get RawInput data
		r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", &uRawInput, "UInt*", sz, "UInt", 8 + (A_PtrSize * 2))
 
		x := NumGet(&uRawInput, offsets.x, "Int")
		y := NumGet(&uRawInput, offsets.y, "Int")
 
		this.Callback.(ThisMouse, x, y)
 
		; There is no message for "Stopped", so simulate one
		fn := this.TimeoutFn
		SetTimer, % fn, -50
	}
 
	TimeoutFunc(){
		this.Callback.(0, 0)
	}
 
}
I will look into building multiple mouse support into the sample script(s), and into UCR.
Last edited by evilC on 11 Dec 2016, 11:28, edited 1 time in total.
zenryou
Posts: 2
Joined: 06 Aug 2016, 13:14

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

06 Aug 2016, 13:16

This would be the biggest help ever to me if you can do it. My ability ends at very basic scripts, but it's cool that apparently it's even doable.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

06 Aug 2016, 17:33

For future reference, this is how it is done:

When you subscribe to mouse movement using the RegisterRawInputDevices Windows API call, you then subscribe to WM_INPUT messages (message 0xFF) using the AHK command OnMessage

OnMessage(0x00FF, "ProcessMessages")

As with all messages, when it gets called, your function gets passed "lParam" and "wParam" params, so your function will look like this:
ProcessMessages(lParam, wParam){

The mouse ID is in a chunk of memory that lParam points to, you can extract it like this (Code ripped basically verbatim from EitherMouse):

Code: Select all

ProcessMessages(lParam, wParam){
	VarSetCapacity(raw, 40, 0)
	If (!DllCall("GetRawInputData",uint,lParam,uint,0x10000003,uint,&raw,"uint*",40,uint, 16) or ErrorLevel)
		Return 0
	ThisMouse := NumGet(raw, 8)
	[...]
}
ThisMouse will then hold an ID which is unique to the mouse that generated this WM_INPUT message, and the number appears to remain consistent across runs and reboots.
zenryou
Posts: 2
Joined: 06 Aug 2016, 13:14

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

07 Aug 2016, 00:25

Awesome, so far that makes sense to me. Hopefully things remain the same after reboots for me as well.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

12 Aug 2016, 12:35

I think I realized a major flaw in some of my sample scripts (eg the mouse to keyboard one).

When you move the mouse, mousedelta will see like x=1, y=0, but what I neglected to realize is that when moving the mouse diagonally, you may get input like this:
x=1, y=1
x=0, y=1
x=1, y=0
x=1, y=1
When getting input like this, i was resetting the axis that got a 0, even though it does not necessarily mean that axis has stopped. The net result is that it would appear to strobe on/off some axes when it should not be.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

12 Aug 2016, 13:04

austin0751 wrote:Is it possible to make this script hold the wasd keys down while the mouse is moving one direction instead of just rapidly pressing the keys?
As per my post above, and in response to the quoted post, I think I have worked out what the problem is.

If anyone could try this new version of the mouse to keyboard script, and let me know if it performs better, I would appreciate it.

Code: Select all

#SingleInstance force
#include MouseDelta.ahk
 
MacroOn := 0
new MouseDelta("MouseEvent")
 
return
 
F12::
	MacroOn := !MacroOn
	return
 
; Gets called when mouse moves or stops
; x and y are DELTA moves (Amount moved since last message), NOT coordinates.
MouseEvent(obj, MouseID){
	;static KeyMap := {u: "w", d: "s", l: "a", r: "d"}		; WSAD
	static KeyMap := {u: "Up", d: "Down", l: "Left", r: "Right"}	; Arrow Keys
	static KeyStates := {u: 0, d: 0, l: 0, r: 0}
	static CompareMap := {u: {op: "<", ax: "y"}, d: {op: ">", ax: "y"}, l: {op: "<", ax: "x"}, r: {op: ">", ax: "x"}}
	global MacroOn
 
	if (!MacroOn)
		return
 
	; Cycle through each direction
	for dir in KeyMap {
		op := CompareMap[dir].op		; Operator. < or >
		ax := obj[CompareMap[dir].ax]	; Value for axis. Will be negative, positive, or 0
		if (!ObjHasKey(obj, CompareMap[dir].ax))
			continue
		if ((op = ">" ? ax > 0 : ax < 0) && !KeyStates[dir]){
			str := "{" KeyMap[dir] " down}"
			;OutputDebug % "AHK| 1) x:" obj.x ", y:" obj.y ", dir: " dir ", state: " KeyStates[dir] " = "str
			Send % str
			KeyStates[dir] := 1
		} else if (!ax && KeyStates[dir]){
			str := "{" KeyMap[dir] " up}"
			;OutputDebug % "AHK| 2) x:" obj.x ", y:" obj.y ", dir: " dir ", state: " KeyStates[dir] " = "str
			Send % str
			KeyStates[dir] := 0
		}
	}
}
 
; Instantiate this class and pass it a func name or a Function Object
; The specified function will be called with the delta move for the X and Y axes
; Normally, there is no windows message "mouse stopped", so one is simulated.
; After 10ms of no mouse movement, the callback is called with 0 for X and Y
Class MouseDelta {
	__New(callback){
		static DevSize := 8 + A_PtrSize
		static RIDEV_INPUTSINK := 0x00000100
 
		this.TimeoutFn := this.TimeoutFunc.Bind(this)
 
		this.Callback := callback
		; Register mouse for WM_INPUT messages.
		VarSetCapacity(RAWINPUTDEVICE, DevSize)
		NumPut(1, RAWINPUTDEVICE, 0, "UShort")
		NumPut(2, RAWINPUTDEVICE, 2, "UShort")
		NumPut(RIDEV_INPUTSINK, RAWINPUTDEVICE, 4, "Uint")
		; WM_INPUT needs a hwnd to route to, so get the hwnd of the AHK Gui.
		; It doesn't matter if the GUI is showing, it still exists
		Gui +hwndhwnd
		NumPut(hwnd, RAWINPUTDEVICE, 8, "Uint")
 
		this.RAWINPUTDEVICE := RAWINPUTDEVICE
		DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
		fn := this.MouseMoved.Bind(this)
		OnMessage(0x00FF, fn)
	}
 
	__Delete(){
		static RIDEV_REMOVE := 0x00000001
		static DevSize := 8 + A_PtrSize
		RAWINPUTDEVICE := this.RAWINPUTDEVICE
		NumPut(RIDEV_REMOVE, RAWINPUTDEVICE, 4, "Uint")
		DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
	}
 
	; Called when the mouse moved.
	; Messages tend to contain small (+/- 1) movements, and happen frequently (~20ms)
	MouseMoved(wParam, lParam){
		Critical
		; RawInput statics
		static DeviceSize := 2 * A_PtrSize, iSize := 0, sz := 0, offsets := {x: (20+A_PtrSize*2), y: (24+A_PtrSize*2)}, uRawInput
 
		static axes := {x: 1, y: 2}
 
		VarSetCapacity(raw, 40, 0)
		If (!DllCall("GetRawInputData",uint,lParam,uint,0x10000003,uint,&raw,"uint*",40,uint, 16) or ErrorLevel)
			Return 0
		ThisMouse := NumGet(raw, 8)

		; Find size of rawinput data - only needs to be run the first time.
		if (!iSize){
			r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", 0, "UInt*", iSize, "UInt", 8 + (A_PtrSize * 2))
			VarSetCapacity(uRawInput, iSize)
		}
		sz := iSize	; param gets overwritten with # of bytes output, so preserve iSize
		; Get RawInput data
		r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", &uRawInput, "UInt*", sz, "UInt", 8 + (A_PtrSize * 2))
 
		x := NumGet(&uRawInput, offsets.x, "Int")
		y := NumGet(&uRawInput, offsets.y, "Int")
		
		obj := {}
		if (x)
			obj.x := x
		if (y)
			obj.y := y
			
 
		this.Callback.(obj, ThisMouse)
 
		; There is no message for "Stopped", so simulate one
		fn := this.TimeoutFn
		SetTimer, % fn, -50
	}
 
	TimeoutFunc(){
		this.Callback.({x: 0, y: 0}, 0)
	}
 
}
User avatar
KilliK
Posts: 255
Joined: 10 Mar 2016, 21:19

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

31 Oct 2016, 06:29

@evilC

thank you for this script. I was looking for something to track the movement of the mouse.
But I have one question. How exactly do I use it as an if-statement to trigger an event when I move the mouse?

for example, I am using a script which confines the mouse cursor to my main monitor. this means that when the cursor reaches the border of my screen, it stops there while my mouse is moving.
what i want to do is that if I keep moving the mouse for 500ms while the cursor is stuck in the border, then my script lets the cursor to move to the second monitor.
so I have to use an IF statement with the logic: if mouse is moving for 500ms AND cursor is in the border THEN ignore the confinement.
I know how to do the "cursor is on the border" part, but I dont know how to do the "mouse is moving for 500ms" part with your library.

can you help me with this? :|
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

31 Oct 2016, 07:22

The RawInput API, as far as I am aware, has no concept of where the cursor is.

What you could probably do is this:
Each time MouseEvent is fired, check the cursor position.
You can pull monitor coords from AHK, so you can calculate eg that the cursor is against the right edge of a monitor and MouseEvent is receiving "Move Right" signals.
MrCrazyheat

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

07 Nov 2016, 10:36

I am sort of new to AHK, but after hours of troubleshooting a script for a game that was using MouseMove, when I completely forgot the mouse stays stationary, and mouse movements are recorded differently. But I have a question.

How do I implement this? You have given examples of implementation for remapping controls and displaying mouse movement, but not a user friendly way of showing how to implement it with MouseMove.

Is there any way for a beginner to do that?
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

07 Nov 2016, 11:25

How you implement what for MouseMove?

This technique is inherently for when you cannot use MouseMove.

MouseMove is, in general, "absolute" - ie "Move the mouse TO the coordinates x, y"
Reading mouse using this library gives you "relative" movement (ie the mouse moved 1 up and 2 left) and is generally best used in tandem with the mouse_event API call which lets you move the mouse in a relative manner .

Yes, there is a "relative" mode for MouseMove, but AFAIK it does not play way with applications (eg mouse aim games) that expect relative input.

If you explain your use-case better, and maybe post some code, then I can possibly help more.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

07 Nov 2016, 11:53

Working script that uses this library to achieve a "Sniper Mode" type DPI shift when you press a button:
Default key to enable sniper mode is F12 - change by editing ShiftKey
Default mode is hold for sniper mode (Can be set to toggle, change the value of ShiftMode)
Scalefactor controls how much less sensitive sniper mode is. 4x is default.

Now one of the sample scripts in the original post. Duplication removed.
Last edited by evilC on 11 Dec 2016, 11:26, edited 3 times in total.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

06 Dec 2016, 10:46

Hi.
I have a few comments.
Someone asked for a mouse recorder in the ask for help section, so I tried to make a simple one.
My idea was, to save the delta info, eg, dx,dy, and then playback is simply MouseMove,dx,dy,0,R.
I realised quite quickly, that if you have any mouse acceleration or sensitivity settings on the mouse, the delta info doesn't match the mouse cursor, this is of course as (should be) expected, I felt stupid.
However, when I disabled these settings, to verify that my mouse recorder worked, in principle at least, I found that still some delta info seemed to be lost.
Since, messages under 0x312 aren't buffered, I figuered some got lost, and tried, as recommended in the docs, to use Critical, 30, it seemed to help a little, but still some info was lost.
Finally, I found, and I think this could be useful for others, that I could change the so called polling rate of my mouse, this was set to 1000 hz, I guess I at some point figured, more is more.
If I set this to 125 hz, nothing is lost, it seems like this guards the (maximum) frequency at which the message 0xFF will be posted.

Another, small thing to, note, if either of the axes doesn't change, its delta value is blank (eg, dx:=""), rather than 0.

Maybe I am missing something, but I never see __Delete() being called, to remedy, I changed and added:

Code: Select all

	__new(){
		;[...]
		this.fn := this.MouseMoved.Bind(this) ; Monitor function.
		this.start() ; To preserve original behaviour
	}
	
	start()
	{
		OnMessage(0x00FF, this.fn) ; Start monitor msg
		return
	}
	
	stop()
	{
		OnMessage(0x00FF, this.fn, 0)	; Stop monitor msg
		return
	}
	
	
	clear()
	{
		this.stop()
		this.fn:=""					; Needs to be cleared before __Delete() can be called
		this.TimeoutFn:=""
		return
	}
Then one should save a reference to the instance, eg

Code: Select all

md:= new MouseDelta("f")
; do stuff
; Now I'm done
md.clear()
md:="" ; Now __Delete() will be called
And finally, I figured that UCR would use this code for the InputDelta, although I couldn't find the code. However, whatever settings I use for my mouse, when doing the mouse recording using InputDelta in UCR, a lot of delta info is lost. Couldn't get it working. I set it up like

Code: Select all

init()
{
	;...
	this.AddControl("InputDelta", "mouse", 0, this.MouseEvent.Bind(this))
	;...
}
MouseEvent(e)
{
	if !this.recording
		return
	dx:=e.axes.x
	dy:=e.axes.y
	; save dx dy  and stuff
	
}
I pasted the MouseDelta class in a UCR plugin and did the exact same thing with the delta info from MouseDelta as with the delta info from InputDelta, MouseDelta records perfectly, InputDelta fails with good margin.
Is it possible to stop the InputDelta from gathering info when not needed, besides from using Clear in its menu? It seems to be making UCR somewhat unresponsive when on.

Sorry for the long post. Cheers!
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

06 Dec 2016, 16:05

UCR's implementation is in Threads\ProfileInputThread.ahk - the class is RawInput_Mouse_Delta
At some point I plan on splitting all the classes in here out into their own files, but for now it lives there.

Good catch regarding the self-refs blocking the destructor, will have to fix that

[Edit]Completely missed the point of what you were saying, yeah, will fix returning ""
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

06 Dec 2016, 18:13

In Threads\ProfileInputThread.ahk -> RawInput_Mouse_Delta.OnMouseMove(wParam, lParam) , I added Critical at the top of the function, now, nothing gets lost, works perfect. However, not sure how it will impact the rest of the program, you might miss other, more important messages. You could keep it in mind for future reference.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

11 Dec 2016, 11:25

Updated the first post with a new version of the library, and updated versions of the demo scripts, plus the Sniper Mode script is now one of the demo scripts.
It is also now compatible with x64 AHK - Thanks to Helgef on that one.

The "Stopped" functionality is gone. The user can implement if they wish, an example is in the Keyboard to Mouse sample script.

The Mouse to Keyboard script is completely re-written with a new "Pool" based system.
unbreakable
Posts: 6
Joined: 26 Mar 2017, 06:07

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

28 Mar 2017, 05:19

I have been redirected here from another thread with my question "Q: Is it possible to remap scroll wheel of particular mouse as vJoy Axis? Also is it posible to remap buttons of particular mouse as vJoy buttons?"

So, before I spend a lot of time learning how to write scripts for AutoHotkey, I want to be sure whether it is possible to remap buttons and scroll wheel axis of particular mouse (I have three mouses but want to apply script for only one of them)? If possible, could you tell me how to do that and help with a script?
I want to use my third mouse's scroll wheel and buttons as additional controller(vJoy) with 1 axis(for scroll wheel - vJoy Axis1) and 3 buttons(Left - vJoy Button1, Right - Button2 and Middle - Button3). I have been trying different software(at least 8 programs) to do that with no success yet.
User avatar
evilC
Posts: 4822
Joined: 27 Feb 2014, 12:30

Re: [Library] MouseDelta - See mouse input the way games see it. (Use for mouse axis->key or joystick)

28 Mar 2017, 05:23

Depends on your definition of "Remap".
If you need it to block the original function (ie make the mouse wheel on one particular device NOT scroll pages etc, in addition to performing the new function), then no, this is currently not possible.
If all you need to do is add another function to the wheel of one mouse, then yes, this is possible.

Return to “Gaming Scripts (v1)”

Who is online

Users browsing this forum: No registered users and 36 guests