Code archives/Networking/Using Membase (or Memcached) from Blitz+ (or Blitz3D)

This code has been declared by its author to be Public Domain code.

Download source code

Using Membase (or Memcached) from Blitz+ (or Blitz3D) by Mahan2011
Memcached (http://memcached.org/): Free & open source, high-performance, distributed memory object caching system.

Membase (http://www.couchbase.org/membase). Similar to Memcached but also provides persistence to disk. Using the "Moxi"-proxy (also on the membase page) the interface is identical to Memcached for the clients.

Using this interface together with Membase + Moxi (the setup I tested) allows for real time operations (like in a game) from BlitzPlus (Blitz+) and B3D.

Note that the game Farmville (by Zynga) uses Membase as it's primary storage.
;---------------------------------------------------------------------
; Basic MemBase/MemCached API for Blitz3D / BlitzPlus
;
; Mattias Hansson (MaHan) - 2011
; 

Type TMembaseConnection
	Field ipaddr$
	Field port%
	Field tcpStream%
	Field anticipatedRecvBytes% ; 0 = we are not waiting for data
	Field tempBank% 			; used when receiving strings
	Field inRequest% 			; Indicates if this connection is currently receiving data from a request
End Type


Function paramNoFromString$(s$, paramNo%, separator$=" ")
	Local currOffset% = 0
	Local paramCount% = 1
	
	Repeat
		Local newOffset = Instr(s, separator, currOffset + 1)
		If newOffset = 0 Then newOffset = Len(s) + 1
		If paramNo = paramCount
			Return Mid(s, currOffset+1, (newOffset - currOffset) -1)
		EndIf
		currOffset = newOffset
		paramCount = paramCount+1
	Until currOffset >= Len(s)
	Return ""
End Function


Function MB_Create.TMembaseConnection(ip$, port% = 11211)
	Local mb.TMembaseConnection = New TMembaseConnection
	mb\ipaddr = ip
	mb\port = port
	mb\tcpStream = OpenTCPStream(mb\ipaddr, mb\port)
	Return mb
End Function

Function MB_Free(mb.TMembaseConnection)
	If mb\tempBank Then FreeBank(mb\tempBank)
	If mb\tcpStream Then CloseTCPStream(mb\tcpStream)
	Delete mb
End Function

Function MB_Set(mb.TMembaseConnection, key$, bankId%)
	MB_internal_clearBuffer(mb)
	WriteLine(mb\tcpStream, "set " + key + " 0 0 " + BankSize(bankId))
	WriteBytes(bankId, mb\tcpStream, 0, BankSize(bankId)-1)
	WriteLine(mb\tcpStream, "")
End Function

Function MB_SetStr(mb.TMembaseConnection, key$, value$)
	MB_internal_clearBuffer(mb)
	WriteLine(mb\tcpStream, "set " + key + " 0 0 " + Len(value))
	WriteLine(mb\tcpStream, value)
End Function

; Keys might be a single key or several separated with whitespace
Function MB_Request(mb.TMembaseConnection, keys$)
	MB_internal_clearBuffer(mb)
	WriteLine(mb\tcpStream, "get " + keys)
	mb\inRequest = True
End Function	

Function MB_Delete(mb.TMembaseConnection, key$)
	MB_internal_clearBuffer(mb)
	WriteLine(mb\tcpStream, "delete " + key)
End Function	

Function MB_Avail(mb.TMembaseConnection)
	Local result = False
	If Not mb\anticipatedRecvBytes Then
		If Not Eof(mb\tcpStream)
			Local answer$=ReadLine(mb\tcpStream)
			
			Select paramNoFromString(answer, 1)
				Case "VALUE"
					mb\anticipatedRecvBytes = Int(paramNoFromString(answer, 4))
					;Print "Set anticipated bytes: " + mb\anticipatedRecvBytes
				Case "END"
					;Print "Ended request"
					mb\inRequest = False
			End Select
			
		EndIf
	EndIf
	;Print "ReadAvail() now: " + ReadAvail(mb\tcpStream)
	Return (mb\anticipatedRecvBytes > 0) And (ReadAvail(mb\tcpStream) >= mb\anticipatedRecvBytes + 2) ; 2 for CRLF
End Function

Function MB_Get(mb.TMembaseConnection, bankId%)
	If mb\anticipatedRecvBytes = 0 Then RuntimeError "MB_Get() - not waiting for answer. Try MB_Requst first?"
	If mb\anticipatedRecvBytes > BankSize(bankId) Then RuntimeError "MB_Get() - trying to stuff to much into passed bank."
	If Not MB_Avail(mb) Then RuntimeError "MB_Get() - Requested value for key is not ready. Try waiting for positive MB_Avail() first?"
	ReadBytes(bankId, mb\tcpStream, 0, mb\anticipatedRecvBytes)
	; remove ending CRLF
	ReadByte(mb\tcpStream)
	ReadByte(mb\tcpStream)
	mb\anticipatedRecvBytes = 0
End Function

Function MB_GetStr$(mb.TMembaseConnection)
	If mb\anticipatedRecvBytes = 0 Then RuntimeError "MB_GetStr$() - not waiting for answer. Try MB_Requst first?"
	If Not mb\tempBank Then mb\tempBank = CreateBank(mb\anticipatedRecvBytes)
	If BankSize(mb\tempBank) < mb\anticipatedRecvBytes Then ResizeBank(mb\tempBank, mb\anticipatedRecvBytes)
	
	Local tempRecvBytes% = mb\anticipatedRecvBytes
	MB_Get(mb, mb\tempBank)
	
	Local i
	Local s$
	For i = 0 To tempRecvBytes-1
		s = s + Chr(PeekByte(mb\tempBank, i))
	Next
	Return s
End Function

Function MB_internal_clearBuffer(mb.TMembaseConnection)
	While ReadAvail(mb\tcpStream)
		ReadLine(mb\tcpStream)
	Wend
End Function

Comments

Mahan2011
Test program:


You might want to edit the server constant to match your Membase server.

Const MEM_SERVER$ = "192.168.0.108"


Note: I've tested this code against Membase + Moxi running in Linux on another machine.


Code Archives Forum